@c4a/context-cli 0.7.14 → 0.7.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +1071 -1241
- package/indexers/contracts/profile-contract.json +245 -245
- package/indexers/release-manifest.json +1 -1
- package/package.json +12 -12
- package/parserEntryWorker.js +8 -0
- package/plugins/VERSION +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/providers/context/manifest.json +20 -20
- package/providers/context/provider.yaml +1 -1
- package/providers/context/resources/dialogue/knowledge-review.md +14 -6
- package/providers/context/resources/manuals/guides/knowledge-updates.md +6 -0
- package/providers/context/resources/manuals/guides/lark-resources.md +38 -0
- package/providers/context/resources/procedures/document-capture.md +7 -0
- package/providers/context/resources/procedures/knowledge-review.md +26 -5
- package/providers/context/resources/procedures/knowledge-updates.md +6 -0
- package/providers/context/resources/procedures/production-requirements.md +23 -0
- package/providers/context/resources/procedures/runtime-event-delivery.md +7 -1
- package/providers/context/resources/procedures/work-start-report.md +72 -0
package/cli.js
CHANGED
|
@@ -41982,6 +41982,12 @@ var getSourceType = (sourceDefinition) => {
|
|
|
41982
41982
|
}), captureLark = (definition) => {
|
|
41983
41983
|
const sourceDefinition = bindSourceType(definition.source, "lark", "captureLark source");
|
|
41984
41984
|
const sourceId = getSourceName(sourceDefinition);
|
|
41985
|
+
for (const key of ["images", "gifs"]) {
|
|
41986
|
+
const value = definition.resources?.[key];
|
|
41987
|
+
if (value !== undefined && value !== "bundle" && value !== "reference-only") {
|
|
41988
|
+
throw new TypeError(`captureLark resources.${key} must be bundle or reference-only`);
|
|
41989
|
+
}
|
|
41990
|
+
}
|
|
41985
41991
|
const maxBytesPerResource = definition.resources?.maxBytesPerResource ?? 20 * 1024 * 1024;
|
|
41986
41992
|
const maxTotalBytes = definition.resources?.maxTotalBytes ?? 200 * 1024 * 1024;
|
|
41987
41993
|
if (!Number.isSafeInteger(maxBytesPerResource) || maxBytesPerResource < 1) {
|
|
@@ -42000,6 +42006,8 @@ var getSourceType = (sourceDefinition) => {
|
|
|
42000
42006
|
writes: [sourceSnapshotResource(sourceDefinition, "lark")],
|
|
42001
42007
|
source: sourceDefinition,
|
|
42002
42008
|
resources: {
|
|
42009
|
+
...definition.resources?.images === undefined ? {} : { images: definition.resources.images },
|
|
42010
|
+
...definition.resources?.gifs === undefined ? {} : { gifs: definition.resources.gifs },
|
|
42003
42011
|
videos: definition.resources?.videos ?? "reference-only",
|
|
42004
42012
|
maxBytesPerResource,
|
|
42005
42013
|
maxTotalBytes
|
|
@@ -64786,6 +64794,8 @@ Restore the authorized source and retry preparation.
|
|
|
64786
64794
|
...scopes.map((scope2) => `- ${scope2.scope}: ${join25(directory, productionSourceFile(scope2.scope))}`),
|
|
64787
64795
|
"",
|
|
64788
64796
|
"Use code skeletons and document outlines to identify the authorized capability families and document tasks, then selectively read full material to decide reader topics. Navigation is not a complete feature inventory. Keep unchecked scope pending; do not parse all code or maintain per-symbol disposition just to plan.",
|
|
64797
|
+
"Configured sources are the knowledge workspace coverage boundary, not a new investigation assignment on every request. First distinguish the user's current task, its actual source dependencies, and unrelated configured sources. Reuse approved content; a source-level pending entry alone does not prove missing knowledge or require new articles.",
|
|
64798
|
+
"Report source baseline/read failures separately from content gaps. For an unrelated configured source, explain that its availability check is unresolved outside this task; do not promise a new code investigation. If the Route still requires resolution, report that precise workflow limitation without deleting source configuration, clearing runtime state, or claiming the source was investigated.",
|
|
64789
64799
|
"Scale planning to the current request. For one or two documents or a clearly bounded module, decide which articles to add or revise and where they belong; do not redesign the whole knowledge base. Start with related existing topics, expand reading only when needed, and reuse applicable decisions. A large module may need several topics, but not investigation of unrelated modules.",
|
|
64790
64800
|
"Separate the whole requested outcome from the current writing batch. Use one batch unless actual dependencies or useful parallel work justify more; do not invent page counts or dependencies. A first useful delivery does not settle remaining authorized work.",
|
|
64791
64801
|
"Use question and brief to describe the reader task and useful depth: a checked file/symbol or document section with a concrete next step for navigation, or the behavior, conditions and steps needed for explanation. Reuse or revise existing articles without replacing valid detail with generic summaries; split distinct tasks, not sources or symbols.",
|
|
@@ -68458,7 +68468,7 @@ function runtimeEventPendingAgentHint(result) {
|
|
|
68458
68468
|
requires_network_access: true,
|
|
68459
68469
|
plan_command: "context logs plan --format json",
|
|
68460
68470
|
command: "context logs flush --format json",
|
|
68461
|
-
message: "Runtime logs are queued locally.
|
|
68471
|
+
message: "Runtime logs are queued locally. If delivery is already authorized and the destination is unchanged, run context logs flush --format json directly. Use the delivery plan when the destination or required host network permission is not yet established."
|
|
68462
68472
|
};
|
|
68463
68473
|
}
|
|
68464
68474
|
function queueContextRuntimeEvent(input) {
|
|
@@ -70284,6 +70294,9 @@ function formatPackageBuildSummary(pkg) {
|
|
|
70284
70294
|
lines.push(` warning: ${warning.path} references ${warning.target}, which ${explanation}.`);
|
|
70285
70295
|
}
|
|
70286
70296
|
const optimization = pkg.resources.delivery.optimization;
|
|
70297
|
+
for (const warning of optimization?.warnings ?? []) {
|
|
70298
|
+
lines.push(` image replaced with placeholder: ${warning.path}: ${warning.reason}`);
|
|
70299
|
+
}
|
|
70287
70300
|
if (optimization?.state === "applied") {
|
|
70288
70301
|
lines.push(` asset optimization: ${optimization.processor}/${optimization.mode}, saved ${optimization.savedBytes} byte(s), largest ${optimization.largestOutputBytes}/${optimization.maxImageBytes} byte(s), total ${optimization.outputBytes}/${optimization.maxTotalImageBytes} byte(s)`);
|
|
70289
70302
|
} else if (pkg.resources.delivery.state === "git-raw") {
|
|
@@ -70766,21 +70779,23 @@ var siteMarkdownConfig, siteThemeScript, siteThemeCss = `
|
|
|
70766
70779
|
.VPNavBarSearch .DocSearch-Button-Container { display: flex; flex: 1; min-width: 0; align-items: center; }
|
|
70767
70780
|
.VPNavBarSearch .DocSearch-Button-Keys { margin-left: auto; }
|
|
70768
70781
|
.VPSidebar { border-top: 1px solid var(--vp-c-divider); border-right: 1px solid var(--vp-c-divider); scrollbar-width: thin; }
|
|
70769
|
-
.VPSidebar .group + .group { border: 0; padding-top:
|
|
70782
|
+
.VPSidebar .group, .VPSidebar .group + .group { border: 0; padding-top: 0; }
|
|
70770
70783
|
.VPSidebarItem .link { min-width: 0; overflow: hidden; }
|
|
70771
70784
|
.VPSidebarItem .text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px !important; line-height: 20px !important; font-weight: 450 !important; }
|
|
70772
|
-
.VPSidebarItem.level-0 { padding-bottom:
|
|
70785
|
+
.VPSidebarItem.level-0 { padding-bottom: 0 !important; }
|
|
70773
70786
|
.VPSidebarItem .indicator { display: none; }
|
|
70774
|
-
.VPSidebarItem .text { padding:
|
|
70775
|
-
.VPSidebarItem .item { min-height:
|
|
70787
|
+
.VPSidebarItem .text { padding: 0 !important; }
|
|
70788
|
+
.VPSidebarItem .item { height: 38px; min-height: 38px; align-items: center; padding: 0 16px 0 26px; border-radius: 0; }
|
|
70789
|
+
.VPSidebarItem.is-link:not(.is-active) > .item > .link > p.text { color: var(--vp-c-text-2); }
|
|
70790
|
+
.VPSidebarItem.is-link:not(.is-active) > .item > .link:hover > p.text { color: var(--vp-c-brand-1); }
|
|
70776
70791
|
.VPSidebarItem.is-active > .item { background: var(--vp-c-brand-soft); box-shadow: none; }
|
|
70777
70792
|
.VPSidebarItem.is-active > .item .text { color: var(--vp-c-brand-1) !important; font-weight: 550 !important; }
|
|
70778
70793
|
.VPSidebarItem .items { margin-left: 0; padding-left: 0 !important; border-left: 0 !important; }
|
|
70779
|
-
.VPSidebarItem.level-1 > .item { padding-left:
|
|
70780
|
-
.VPSidebarItem.level-2 > .item { padding-left:
|
|
70781
|
-
.VPSidebarItem.level-3 > .item { padding-left:
|
|
70782
|
-
.VPSidebarItem.level-4 > .item { padding-left:
|
|
70783
|
-
.VPSidebarItem.level-5 > .item { padding-left:
|
|
70794
|
+
.VPSidebarItem.level-1 > .item { padding-left: 40px; }
|
|
70795
|
+
.VPSidebarItem.level-2 > .item { padding-left: 54px; }
|
|
70796
|
+
.VPSidebarItem.level-3 > .item { padding-left: 68px; }
|
|
70797
|
+
.VPSidebarItem.level-4 > .item { padding-left: 82px; }
|
|
70798
|
+
.VPSidebarItem.level-5 > .item { padding-left: 96px; }
|
|
70784
70799
|
.VPSidebar .group { width: 100% !important; }
|
|
70785
70800
|
.VPSidebarItem .item:hover { background: var(--vp-c-default-soft); }
|
|
70786
70801
|
.VPDoc .container, .VPDoc > .container > .content, .VPDoc .content-container { max-width: none !important; min-width: 0 !important; }
|
|
@@ -71511,15 +71526,23 @@ function sidebar(entries2) {
|
|
|
71511
71526
|
return entries2.map((entry) => ({
|
|
71512
71527
|
text: entry.title,
|
|
71513
71528
|
...entry.href ? { link: entry.href } : {},
|
|
71514
|
-
|
|
71529
|
+
items: sidebar(entry.children),
|
|
71530
|
+
...entry.children.length ? { collapsed: true } : {}
|
|
71515
71531
|
}));
|
|
71516
71532
|
}
|
|
71517
71533
|
function siteSections(entries2) {
|
|
71534
|
+
const directoriesFirst = (siblings) => {
|
|
71535
|
+
const ordered = [
|
|
71536
|
+
...siblings.filter((entry) => entry.children.length > 0),
|
|
71537
|
+
...siblings.filter((entry) => entry.children.length === 0)
|
|
71538
|
+
];
|
|
71539
|
+
return ordered.map((entry) => ({ ...entry, children: directoriesFirst(entry.children) }));
|
|
71540
|
+
};
|
|
71518
71541
|
const pageLinks = (entry) => [
|
|
71519
71542
|
...entry.href ? [entry.href.split("#")[0]] : [],
|
|
71520
71543
|
...entry.children.flatMap(pageLinks)
|
|
71521
71544
|
];
|
|
71522
|
-
return entries2.map((entry) => ({
|
|
71545
|
+
return entries2.map((entry) => ({ ...entry, children: directoriesFirst(entry.children) })).map((entry) => ({
|
|
71523
71546
|
key: entry.key,
|
|
71524
71547
|
title: entry.title,
|
|
71525
71548
|
href: `/sections/${createHash16("sha256").update(entry.key).digest("hex").slice(0, 20)}.html`,
|
|
@@ -72176,7 +72199,11 @@ async function loadSharpProcessor() {
|
|
|
72176
72199
|
}
|
|
72177
72200
|
return {
|
|
72178
72201
|
async optimize(bytes, definition2) {
|
|
72179
|
-
|
|
72202
|
+
const image = sharp(bytes, { failOn: "error", animated: false });
|
|
72203
|
+
const metadata = await image.metadata();
|
|
72204
|
+
if ((metadata.pages ?? 1) > 1)
|
|
72205
|
+
return bytes;
|
|
72206
|
+
let pipeline2 = image.rotate();
|
|
72180
72207
|
if (definition2.maxDimension !== undefined) {
|
|
72181
72208
|
pipeline2 = pipeline2.resize({
|
|
72182
72209
|
width: definition2.maxDimension,
|
|
@@ -72200,6 +72227,8 @@ async function adaptiveVariants(input) {
|
|
|
72200
72227
|
let smallest = input.asset.bytes.byteLength;
|
|
72201
72228
|
for (const definition2 of input.definitions) {
|
|
72202
72229
|
const output = await input.processor.optimize(input.asset.bytes, definition2);
|
|
72230
|
+
if (output === input.asset.bytes)
|
|
72231
|
+
break;
|
|
72203
72232
|
if (!isWebp(output)) {
|
|
72204
72233
|
throw new ContextError(ExitCode.WorkspaceStateError, "package asset optimizer returned invalid WebP bytes", {
|
|
72205
72234
|
category: ErrorCategory.WorkspaceStateInvalid,
|
|
@@ -72215,17 +72244,6 @@ async function adaptiveVariants(input) {
|
|
|
72215
72244
|
}
|
|
72216
72245
|
return variants;
|
|
72217
72246
|
}
|
|
72218
|
-
function budgetError(input) {
|
|
72219
|
-
return new ContextError(ExitCode.WorkspaceStateError, "bundled images cannot meet the package size budget", {
|
|
72220
|
-
category: ErrorCategory.WorkspaceStateInvalid,
|
|
72221
|
-
reason_code: "package.assets.image-budget-exceeded",
|
|
72222
|
-
output_bytes: input.outputBytes,
|
|
72223
|
-
max_image_bytes: input.maxImageBytes,
|
|
72224
|
-
max_total_image_bytes: input.maxTotalImageBytes,
|
|
72225
|
-
oversized_paths: input.oversized.map((asset) => asset.packageRelPath),
|
|
72226
|
-
next: "Reduce or replace the reported source images, then rerun context build."
|
|
72227
|
-
});
|
|
72228
|
-
}
|
|
72229
72247
|
async function optimizePackageAssetFiles(input) {
|
|
72230
72248
|
const maxImageBytes = input.maxImageBytes ?? PACKAGE_ASSET_MAX_IMAGE_BYTES;
|
|
72231
72249
|
const maxTotalImageBytes = input.maxTotalImageBytes ?? PACKAGE_ASSET_MAX_TOTAL_IMAGE_BYTES;
|
|
@@ -72251,23 +72269,38 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72251
72269
|
const processor = input.processor ?? await loadSharpProcessor();
|
|
72252
72270
|
const definitions = input.definition === undefined ? DEFAULT_OPTIMIZATION_PROFILES : [input.definition];
|
|
72253
72271
|
const variantsByPath = new Map;
|
|
72272
|
+
const omittedImages = new Set;
|
|
72273
|
+
const warnings = [];
|
|
72254
72274
|
for (const asset of candidates) {
|
|
72255
|
-
|
|
72275
|
+
try {
|
|
72276
|
+
variantsByPath.set(asset.packageRelPath, await adaptiveVariants({ asset, processor, definitions }));
|
|
72277
|
+
} catch (error) {
|
|
72278
|
+
if (error instanceof ContextError)
|
|
72279
|
+
throw error;
|
|
72280
|
+
warnings.push({ path: asset.packageRelPath, reason: error instanceof Error ? error.message : String(error) });
|
|
72281
|
+
omittedImages.add(asset.packageRelPath);
|
|
72282
|
+
variantsByPath.set(asset.packageRelPath, [{ bytes: asset.bytes }]);
|
|
72283
|
+
}
|
|
72256
72284
|
}
|
|
72257
72285
|
const selectedIndex = new Map;
|
|
72258
72286
|
for (const asset of candidates) {
|
|
72259
72287
|
const variants = variantsByPath.get(asset.packageRelPath);
|
|
72288
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72289
|
+
continue;
|
|
72260
72290
|
let index2 = input.definition !== undefined && variants.length > 1 ? 1 : 0;
|
|
72261
72291
|
if (asset.bytes.byteLength > maxImageBytes) {
|
|
72262
72292
|
const fitting = variants.findIndex((variant) => variant.bytes.byteLength <= maxImageBytes);
|
|
72263
72293
|
if (fitting < 0) {
|
|
72264
|
-
|
|
72294
|
+
omittedImages.add(asset.packageRelPath);
|
|
72295
|
+
warnings.push({ path: asset.packageRelPath, reason: "Image exceeds the per-image delivery budget after optimization" });
|
|
72265
72296
|
}
|
|
72266
|
-
index2 = fitting;
|
|
72297
|
+
index2 = Math.max(0, fitting);
|
|
72267
72298
|
}
|
|
72268
72299
|
selectedIndex.set(asset.packageRelPath, index2);
|
|
72269
72300
|
}
|
|
72270
72301
|
const selectedBytes = (asset) => {
|
|
72302
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72303
|
+
return new Uint8Array;
|
|
72271
72304
|
const variants = variantsByPath.get(asset.packageRelPath);
|
|
72272
72305
|
return variants[selectedIndex.get(asset.packageRelPath) ?? 0].bytes;
|
|
72273
72306
|
};
|
|
@@ -72275,6 +72308,8 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72275
72308
|
while (outputBytes > maxTotalImageBytes) {
|
|
72276
72309
|
let best;
|
|
72277
72310
|
for (const asset of candidates) {
|
|
72311
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72312
|
+
continue;
|
|
72278
72313
|
const variants = variantsByPath.get(asset.packageRelPath);
|
|
72279
72314
|
const currentIndex = selectedIndex.get(asset.packageRelPath) ?? 0;
|
|
72280
72315
|
const nextIndex = currentIndex + 1;
|
|
@@ -72286,7 +72321,13 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72286
72321
|
best = { asset, nextIndex, saving };
|
|
72287
72322
|
}
|
|
72288
72323
|
if (best === undefined) {
|
|
72289
|
-
|
|
72324
|
+
const largest = candidates.filter((asset) => !omittedImages.has(asset.packageRelPath)).sort((a, b) => selectedBytes(b).byteLength - selectedBytes(a).byteLength || a.packageRelPath.localeCompare(b.packageRelPath))[0];
|
|
72325
|
+
if (largest === undefined)
|
|
72326
|
+
break;
|
|
72327
|
+
outputBytes -= selectedBytes(largest).byteLength;
|
|
72328
|
+
omittedImages.add(largest.packageRelPath);
|
|
72329
|
+
warnings.push({ path: largest.packageRelPath, reason: "Image exceeds the total delivery budget after optimization" });
|
|
72330
|
+
continue;
|
|
72290
72331
|
}
|
|
72291
72332
|
selectedIndex.set(best.asset.packageRelPath, best.nextIndex);
|
|
72292
72333
|
outputBytes -= best.saving;
|
|
@@ -72294,6 +72335,8 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72294
72335
|
const optimizedByInputPath = new Map;
|
|
72295
72336
|
const optimizedTargetByOriginal = new Map;
|
|
72296
72337
|
for (const asset of candidates) {
|
|
72338
|
+
if (omittedImages.has(asset.packageRelPath))
|
|
72339
|
+
continue;
|
|
72297
72340
|
const output = selectedBytes(asset);
|
|
72298
72341
|
if (output.byteLength >= asset.bytes.byteLength)
|
|
72299
72342
|
continue;
|
|
@@ -72301,14 +72344,14 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72301
72344
|
optimizedByInputPath.set(asset.packageRelPath, { ...asset, packageRelPath, bytes: output });
|
|
72302
72345
|
optimizedTargetByOriginal.set(asset.packageRelPath, packageRelPath);
|
|
72303
72346
|
}
|
|
72304
|
-
const assets = input.assets.map((asset) => optimizedByInputPath.get(asset.packageRelPath) ?? asset);
|
|
72305
|
-
outputBytes = candidates.reduce((sum, asset) => sum + (optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength), 0);
|
|
72306
|
-
const largestOutputBytes = Math.max(0, ...candidates.map((asset) => optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength));
|
|
72347
|
+
const assets = input.assets.filter((asset) => !omittedImages.has(asset.packageRelPath)).map((asset) => optimizedByInputPath.get(asset.packageRelPath) ?? asset);
|
|
72348
|
+
outputBytes = candidates.filter((asset) => !omittedImages.has(asset.packageRelPath)).reduce((sum, asset) => sum + (optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength), 0);
|
|
72349
|
+
const largestOutputBytes = Math.max(0, ...candidates.filter((asset) => !omittedImages.has(asset.packageRelPath)).map((asset) => optimizedByInputPath.get(asset.packageRelPath)?.bytes.byteLength ?? asset.bytes.byteLength));
|
|
72307
72350
|
return {
|
|
72308
72351
|
assets,
|
|
72309
72352
|
optimizedTargetByOriginal,
|
|
72310
72353
|
summary: {
|
|
72311
|
-
state: optimizedByInputPath.size > 0 ? "applied" : "configured-no-benefit",
|
|
72354
|
+
state: omittedImages.size > 0 ? "partial" : optimizedByInputPath.size > 0 ? "applied" : "configured-no-benefit",
|
|
72312
72355
|
candidateFiles: candidates.length,
|
|
72313
72356
|
originalBytes,
|
|
72314
72357
|
outputBytes,
|
|
@@ -72316,6 +72359,8 @@ async function optimizePackageAssetFiles(input) {
|
|
|
72316
72359
|
maxImageBytes,
|
|
72317
72360
|
maxTotalImageBytes,
|
|
72318
72361
|
largestOutputBytes,
|
|
72362
|
+
...warnings.length === 0 ? {} : { warnings },
|
|
72363
|
+
...omittedImages.size === 0 ? {} : { omittedImages: [...omittedImages] },
|
|
72319
72364
|
processor: "sharp",
|
|
72320
72365
|
mode: input.definition?.mode ?? "webp"
|
|
72321
72366
|
}
|
|
@@ -72518,6 +72563,7 @@ async function deliverPackageAssetFiles(input) {
|
|
|
72518
72563
|
return {
|
|
72519
72564
|
assets: optimization.assets,
|
|
72520
72565
|
targetByOriginal: optimization.optimizedTargetByOriginal,
|
|
72566
|
+
...optimization.summary.omittedImages === undefined ? {} : { omittedImages: optimization.summary.omittedImages },
|
|
72521
72567
|
summary: {
|
|
72522
72568
|
state: "bundled",
|
|
72523
72569
|
sourceFiles: input.assets.length,
|
|
@@ -72715,7 +72761,15 @@ async function writeSelectedPackageKnowledge(input) {
|
|
|
72715
72761
|
const results = await Promise.allSettled(projectedPages.slice(offset, offset + 8).map(async (projected) => {
|
|
72716
72762
|
assertSafeRenderedPath2(projected.pageOutputPath, "knowledge path");
|
|
72717
72763
|
const outputPath = join62(input.projectRoot, input.pkg.outDir, projected.pageOutputPath);
|
|
72718
|
-
|
|
72764
|
+
let mediaContent = projected.content;
|
|
72765
|
+
const omitted = new Set((delivered.omittedImages ?? []).map((path2) => packageMarkdownTarget(projected.pageOutputPath, path2)));
|
|
72766
|
+
for (const link of markdownReaderLinks(mediaContent).reverse()) {
|
|
72767
|
+
if (!omitted.has(link.target))
|
|
72768
|
+
continue;
|
|
72769
|
+
const label2 = link.label.replace(/[<>\[\]_*`]/gu, "");
|
|
72770
|
+
mediaContent = mediaContent.slice(0, link.start) + `[Image omitted: ${label2 || "image"}; see article sources]` + mediaContent.slice(link.end);
|
|
72771
|
+
}
|
|
72772
|
+
const rewritten = replaceMarkdownInlineLinkTargets(mediaContent, (link) => {
|
|
72719
72773
|
for (const [inputPath, outputPath2] of delivered.targetByOriginal) {
|
|
72720
72774
|
if (link.target === packageMarkdownTarget(projected.pageOutputPath, inputPath)) {
|
|
72721
72775
|
return /^https:\/\//u.test(outputPath2) ? outputPath2 : packageMarkdownTarget(projected.pageOutputPath, outputPath2);
|
|
@@ -82592,7 +82646,7 @@ async function productionWorkflowRoute(input) {
|
|
|
82592
82646
|
revision,
|
|
82593
82647
|
reason_code: resolved.reasonCode,
|
|
82594
82648
|
availability: resolved.availability,
|
|
82595
|
-
summary: report ? `Present the report and wait. After approval, write {stage: ${stage.id}, decision: approved} to ${path2}.` : writing ? "Read the issued task directories. Coordinate them sequentially unless this caller supports independent Agents; only the coordinator submits shared state." : repair ? "Add revision tasks for rejected articles using the Review feedback; accepted production responsibilities remain unchanged." : investigate ? `
|
|
82649
|
+
summary: report ? `Present the report and wait. After approval, write {stage: ${stage.id}, decision: approved} to ${path2}.` : writing ? "Read the issued task directories. Coordinate them sequentially unless this caller supports independent Agents; only the coordinator submits shared state." : repair ? "Add revision tasks for rejected articles using the Review feedback; accepted production responsibilities remain unchanged." : investigate ? `Review pending configured scopes: ${stage.pending_scopes.filter((scope2) => !stage.gaps.some((gap2) => gap2.scope === scope2)).join(", ")}. Check their relevance to the current request and existing approved content before assigning investigation. Submit supported article tasks and remaining pending_scopes; do not infer missing articles from this list or repeat accepted work.` : resolved.node === "resolve-production-gap" ? `Source availability gaps: ${stage.gaps.map((gap2) => `${gap2.scope}: ${gap2.reason}`).join("; ")}. These failures do not establish missing knowledge or a new investigation assignment. Identify which sources the current task actually depends on; report unrelated configured-source failures separately. Preserve the configuration and follow the current resolution action.` : "Prepare the current stage's eligible task directories.",
|
|
82596
82650
|
commands: report || prepare || writing || repair || investigate || gap ? [{
|
|
82597
82651
|
command: command2,
|
|
82598
82652
|
effect: "write",
|
|
@@ -86530,7 +86584,7 @@ class FidelityTracker {
|
|
|
86530
86584
|
var LARK_EMPTY_SUB_PAGE_LIST_CODE = "lark.capture.sub-page-list-empty";
|
|
86531
86585
|
|
|
86532
86586
|
// src/lib/larkDocxResources.ts
|
|
86533
|
-
import { createHash as
|
|
86587
|
+
import { createHash as createHash25 } from "node:crypto";
|
|
86534
86588
|
function elementName(node3) {
|
|
86535
86589
|
return Object.keys(node3).find((key) => key !== ":@" && key !== "#text");
|
|
86536
86590
|
}
|
|
@@ -86606,18 +86660,18 @@ function resourceIdentity(kind, attrs) {
|
|
|
86606
86660
|
];
|
|
86607
86661
|
return candidates.find((value) => value !== undefined && value.length > 0 && !isTransientLarkMediaUrl(value));
|
|
86608
86662
|
}
|
|
86609
|
-
function registerLarkResource(ctx, blockType, kind, attrs,
|
|
86663
|
+
function registerLarkResource(ctx, blockType, kind, attrs, title2) {
|
|
86610
86664
|
const identity = resourceIdentity(kind, attrs);
|
|
86611
86665
|
const href = attrs.href ?? attrs.url;
|
|
86612
86666
|
const locator = identity !== undefined ? `lark:${kind}:${identity}` : href !== undefined && !isTransientLarkMediaUrl(href) ? href : undefined;
|
|
86613
|
-
const resolvedLocator = locator ?? `lark:${kind}:unresolved:${
|
|
86667
|
+
const resolvedLocator = locator ?? `lark:${kind}:unresolved:${createHash25("sha256").update(JSON.stringify(Object.fromEntries(Object.entries(attrs).sort(([left], [right]) => left.localeCompare(right))))).digest("hex").slice(0, 12)}`;
|
|
86614
86668
|
if (locator === undefined) {
|
|
86615
86669
|
ctx.tracker.flag(blockType, "resource has no stable token, source id, or non-transient URL", "error");
|
|
86616
86670
|
}
|
|
86617
86671
|
const resource = {
|
|
86618
86672
|
kind,
|
|
86619
86673
|
locator: resolvedLocator,
|
|
86620
|
-
...
|
|
86674
|
+
...title2 !== undefined && title2.length > 0 ? { title: title2 } : {},
|
|
86621
86675
|
attributes: Object.fromEntries(Object.entries(attrs).filter(([, value]) => value.length > 0 && !isTransientLarkMediaUrl(value)))
|
|
86622
86676
|
};
|
|
86623
86677
|
ctx.resources.push(resource);
|
|
@@ -86635,32 +86689,32 @@ function renderLarkResource(name3, nodes, attrs, ctx) {
|
|
|
86635
86689
|
if (name3 === "cite") {
|
|
86636
86690
|
if (attrs.type === "user")
|
|
86637
86691
|
return `@${attrs["user-name"] ?? (normalizeInline(textContent(nodes)) || "user")}`;
|
|
86638
|
-
const
|
|
86692
|
+
const title3 = attrs.title ?? (normalizeInline(textContent(nodes)) || "Referenced document");
|
|
86639
86693
|
const docId = attrs["doc-id"] ?? attrs.token;
|
|
86640
|
-
const resource2 = registerLarkResource(ctx, name3, "cite", attrs,
|
|
86694
|
+
const resource2 = registerLarkResource(ctx, name3, "cite", attrs, title3);
|
|
86641
86695
|
if (docId === undefined) {
|
|
86642
86696
|
ctx.tracker.flag(name3, "cite has no doc-id or token", "error");
|
|
86643
|
-
return `${escapeMarkdownLabel(
|
|
86697
|
+
return `${escapeMarkdownLabel(title3)} <!-- ${resource2.locator} -->`;
|
|
86644
86698
|
}
|
|
86645
|
-
return `[${escapeMarkdownLabel(
|
|
86699
|
+
return `[${escapeMarkdownLabel(title3)}](${stableDocumentUrl(ctx.sourceUrl, attrs["file-type"], docId)}) <!-- ${resource2.locator} -->`;
|
|
86646
86700
|
}
|
|
86647
86701
|
if (name3 === "img" || name3 === "image") {
|
|
86648
|
-
const
|
|
86649
|
-
const resource2 = registerLarkResource(ctx, name3, "image", attrs,
|
|
86702
|
+
const title3 = attrs.alt ?? attrs.name ?? "Image";
|
|
86703
|
+
const resource2 = registerLarkResource(ctx, name3, "image", attrs, title3);
|
|
86650
86704
|
return `
|
|
86651
86705
|
|
|
86652
|
-
> Image: ${
|
|
86706
|
+
> Image: ${title3} (${resource2.locator})
|
|
86653
86707
|
|
|
86654
86708
|
`;
|
|
86655
86709
|
}
|
|
86656
86710
|
if (name3 === "source" || name3 === "file" || name3 === "attachment") {
|
|
86657
86711
|
const isVideo = attrs.mime?.startsWith("video/") === true || attrs.type === "video";
|
|
86658
86712
|
const kind2 = isVideo ? "video" : "file";
|
|
86659
|
-
const
|
|
86660
|
-
const resource2 = registerLarkResource(ctx, name3, kind2, attrs,
|
|
86713
|
+
const title3 = attrs.name ?? (isVideo ? "Video" : "File");
|
|
86714
|
+
const resource2 = registerLarkResource(ctx, name3, kind2, attrs, title3);
|
|
86661
86715
|
return `
|
|
86662
86716
|
|
|
86663
|
-
> ${isVideo ? "Video" : "File"}: ${
|
|
86717
|
+
> ${isVideo ? "Video" : "File"}: ${title3} (${resource2.locator})
|
|
86664
86718
|
|
|
86665
86719
|
`;
|
|
86666
86720
|
}
|
|
@@ -86680,7 +86734,7 @@ ${safeFence(source2, attrs.type === "mermaid" ? "mermaid" : "text")}
|
|
|
86680
86734
|
}
|
|
86681
86735
|
if (name3 === "diagram") {
|
|
86682
86736
|
const source2 = textContent(nodes).trim();
|
|
86683
|
-
const resourceAttrs = source2.length > 0 ? { ...attrs, "content-hash":
|
|
86737
|
+
const resourceAttrs = source2.length > 0 ? { ...attrs, "content-hash": createHash25("sha256").update(source2, "utf8").digest("hex") } : attrs;
|
|
86684
86738
|
const resource2 = registerLarkResource(ctx, name3, "diagram", resourceAttrs, attrs.title ?? "Diagram");
|
|
86685
86739
|
if (source2.length > 0) {
|
|
86686
86740
|
resource2.inline_content = true;
|
|
@@ -86699,92 +86753,92 @@ ${safeFence(source2, attrs.type ?? "text")}
|
|
|
86699
86753
|
`;
|
|
86700
86754
|
}
|
|
86701
86755
|
if (name3 === "chat_card") {
|
|
86702
|
-
const
|
|
86703
|
-
const resource2 = registerLarkResource(ctx, name3, "chat", attrs,
|
|
86756
|
+
const title3 = attrs.name ?? "Chat";
|
|
86757
|
+
const resource2 = registerLarkResource(ctx, name3, "chat", attrs, title3);
|
|
86704
86758
|
return `
|
|
86705
86759
|
|
|
86706
|
-
> Chat: ${
|
|
86760
|
+
> Chat: ${title3} (${resource2.locator})
|
|
86707
86761
|
|
|
86708
86762
|
`;
|
|
86709
86763
|
}
|
|
86710
86764
|
if (name3 === "readonly-block") {
|
|
86711
|
-
const
|
|
86765
|
+
const title3 = attrs.type ?? "Read-only embedded block";
|
|
86712
86766
|
const kind2 = attrs.type === "diagram" ? "diagram" : "embed";
|
|
86713
|
-
const resource2 = registerLarkResource(ctx, name3, kind2, attrs,
|
|
86767
|
+
const resource2 = registerLarkResource(ctx, name3, kind2, attrs, title3);
|
|
86714
86768
|
return kind2 === "diagram" ? `
|
|
86715
86769
|
|
|
86716
86770
|
> Diagram: ${resource2.locator}
|
|
86717
86771
|
|
|
86718
86772
|
` : `
|
|
86719
86773
|
|
|
86720
|
-
> Embedded block: ${
|
|
86774
|
+
> Embedded block: ${title3} (${resource2.locator})
|
|
86721
86775
|
|
|
86722
86776
|
`;
|
|
86723
86777
|
}
|
|
86724
86778
|
const kind = name3 === "sheet" ? "sheet" : "base";
|
|
86725
|
-
const
|
|
86726
|
-
const resource = registerLarkResource(ctx, name3, kind, attrs,
|
|
86779
|
+
const title2 = attrs.title ?? (kind === "sheet" ? "Embedded Sheet" : "Embedded Base");
|
|
86780
|
+
const resource = registerLarkResource(ctx, name3, kind, attrs, title2);
|
|
86727
86781
|
const details = [attrs["table-id"], attrs["sheet-id"], attrs["view-id"]].filter(Boolean).join(" / ");
|
|
86728
86782
|
return `
|
|
86729
86783
|
|
|
86730
|
-
> ${
|
|
86784
|
+
> ${title2}${details.length > 0 ? ` — ${details}` : ""} (${resource.locator})
|
|
86731
86785
|
|
|
86732
86786
|
`;
|
|
86733
86787
|
}
|
|
86734
86788
|
function renderLarkSubPage(nodes, attrs, ctx) {
|
|
86735
|
-
const
|
|
86789
|
+
const title2 = attrs.title ?? (normalizeInline(textContent(nodes)) || "Untitled subpage");
|
|
86736
86790
|
const docId = attrs["doc-id"] ?? attrs.token;
|
|
86737
|
-
const resource = registerLarkResource(ctx, "sub-page", "document", attrs,
|
|
86791
|
+
const resource = registerLarkResource(ctx, "sub-page", "document", attrs, title2);
|
|
86738
86792
|
if (docId === undefined) {
|
|
86739
86793
|
ctx.tracker.flag("sub-page", "sub-page has no doc-id or token", "error");
|
|
86740
|
-
return `${escapeMarkdownLabel(
|
|
86794
|
+
return `${escapeMarkdownLabel(title2)} <!-- ${resource.locator} -->`;
|
|
86741
86795
|
}
|
|
86742
|
-
return `[${escapeMarkdownLabel(
|
|
86796
|
+
return `[${escapeMarkdownLabel(title2)}](${stableDocumentUrl(ctx.sourceUrl, attrs["file-type"], docId)}) <!-- ${resource.locator} -->`;
|
|
86743
86797
|
}
|
|
86744
86798
|
function renderLarkBookmark(nodes, attrs, ctx) {
|
|
86745
86799
|
const href = attrs.href ?? attrs.url;
|
|
86746
|
-
const
|
|
86747
|
-
const resource = registerLarkResource(ctx, "bookmark", "bookmark", attrs,
|
|
86800
|
+
const title2 = attrs.name ?? attrs.title ?? (normalizeInline(textContent(nodes)) || href) ?? "Bookmark";
|
|
86801
|
+
const resource = registerLarkResource(ctx, "bookmark", "bookmark", attrs, title2);
|
|
86748
86802
|
if (href === undefined || isTransientLarkMediaUrl(href)) {
|
|
86749
86803
|
ctx.tracker.flag("bookmark", "bookmark has no stable non-transient URL", "error");
|
|
86750
86804
|
return `
|
|
86751
86805
|
|
|
86752
|
-
> Bookmark: ${escapeMarkdownLabel(
|
|
86806
|
+
> Bookmark: ${escapeMarkdownLabel(title2)} <!-- ${resource.locator} -->
|
|
86753
86807
|
|
|
86754
86808
|
`;
|
|
86755
86809
|
}
|
|
86756
86810
|
return `
|
|
86757
86811
|
|
|
86758
|
-
> Bookmark: [${escapeMarkdownLabel(
|
|
86812
|
+
> Bookmark: [${escapeMarkdownLabel(title2)}](${href}) <!-- ${resource.locator} -->
|
|
86759
86813
|
|
|
86760
86814
|
`;
|
|
86761
86815
|
}
|
|
86762
86816
|
function renderLarkSyncedReference(attrs, ctx) {
|
|
86763
86817
|
const sourceToken = attrs["src-token"];
|
|
86764
86818
|
const sourceBlockId = attrs["src-block-id"];
|
|
86765
|
-
const
|
|
86766
|
-
const resource = registerLarkResource(ctx, "synced_reference", "synced-reference", attrs,
|
|
86819
|
+
const title2 = attrs.title ?? attrs.name ?? "Synced reference";
|
|
86820
|
+
const resource = registerLarkResource(ctx, "synced_reference", "synced-reference", attrs, title2);
|
|
86767
86821
|
if (sourceToken === undefined || sourceBlockId === undefined) {
|
|
86768
86822
|
ctx.tracker.flag("synced_reference", "synced_reference requires both src-token and src-block-id", "error");
|
|
86769
86823
|
return `
|
|
86770
86824
|
|
|
86771
|
-
> ${escapeMarkdownLabel(
|
|
86825
|
+
> ${escapeMarkdownLabel(title2)} <!-- ${resource.locator} -->
|
|
86772
86826
|
|
|
86773
86827
|
`;
|
|
86774
86828
|
}
|
|
86775
86829
|
const target = `${stableDocumentUrl(ctx.sourceUrl, "docx", sourceToken)}#${encodeURIComponent(sourceBlockId)}`;
|
|
86776
86830
|
return `
|
|
86777
86831
|
|
|
86778
|
-
> [${escapeMarkdownLabel(
|
|
86832
|
+
> [${escapeMarkdownLabel(title2)}](${target}) <!-- ${resource.locator} -->
|
|
86779
86833
|
|
|
86780
86834
|
`;
|
|
86781
86835
|
}
|
|
86782
86836
|
var init_larkDocxResources = () => {};
|
|
86783
86837
|
|
|
86784
86838
|
// src/lib/larkDocxXml.ts
|
|
86785
|
-
import { createHash as
|
|
86839
|
+
import { createHash as createHash26 } from "node:crypto";
|
|
86786
86840
|
function sha256(value) {
|
|
86787
|
-
return `sha256:${
|
|
86841
|
+
return `sha256:${createHash26("sha256").update(value, "utf8").digest("hex")}`;
|
|
86788
86842
|
}
|
|
86789
86843
|
function elementName2(node3) {
|
|
86790
86844
|
return Object.keys(node3).find((key) => key !== ":@" && key !== "#text");
|
|
@@ -86835,9 +86889,9 @@ function renderList(nodes, ctx, ordered) {
|
|
|
86835
86889
|
const name3 = elementName2(item);
|
|
86836
86890
|
ctx.tracker.discover(name3);
|
|
86837
86891
|
ctx.tracker.convert(name3);
|
|
86838
|
-
const
|
|
86892
|
+
const body2 = normalizeMarkdown(renderChildren(elementChildren2(item, name3), { ...ctx, mode: "inline" }));
|
|
86839
86893
|
const marker = ordered ? `${index2 + 1}.` : "-";
|
|
86840
|
-
return
|
|
86894
|
+
return body2.split(`
|
|
86841
86895
|
`).map((line, lineIndex) => lineIndex === 0 ? `${marker} ${line}` : ` ${line}`).join(`
|
|
86842
86896
|
`);
|
|
86843
86897
|
}).join(`
|
|
@@ -86913,15 +86967,15 @@ function renderChecklistItem(blockType, nodes, attrs, ctx) {
|
|
|
86913
86967
|
const checked = attrs.checked;
|
|
86914
86968
|
const state = done ?? checked;
|
|
86915
86969
|
const stateIsValid = (state === "true" || state === "false") && (done === undefined || checked === undefined || done === checked);
|
|
86916
|
-
const
|
|
86970
|
+
const body2 = normalizeInline2(renderChildren(nodes, { ...ctx, mode: "inline" }));
|
|
86917
86971
|
if (!stateIsValid) {
|
|
86918
86972
|
ctx.tracker.flag(blockType, `${blockType} requires one unambiguous boolean done or checked attribute`, "warning", "lark.capture.checkbox-state-invalid", "projection");
|
|
86919
86973
|
return `
|
|
86920
|
-
- [?] ${
|
|
86974
|
+
- [?] ${body2}
|
|
86921
86975
|
`;
|
|
86922
86976
|
}
|
|
86923
86977
|
return `
|
|
86924
|
-
- [${state === "true" ? "x" : " "}] ${
|
|
86978
|
+
- [${state === "true" ? "x" : " "}] ${body2}
|
|
86925
86979
|
`;
|
|
86926
86980
|
}
|
|
86927
86981
|
function meaningfulAttributes(attrs, excluded2) {
|
|
@@ -86931,8 +86985,8 @@ function auditableAttributes(attrs) {
|
|
|
86931
86985
|
return Object.entries(attrs).filter(([, value]) => value.length > 0).map(([key, value]) => [key, isTransientLarkMediaUrl(value) ? "[redacted-transient-url]" : value]).sort(([left], [right]) => left.localeCompare(right));
|
|
86932
86986
|
}
|
|
86933
86987
|
function renderPollOption(blockType, nodes, attrs, ctx) {
|
|
86934
|
-
const
|
|
86935
|
-
const label2 =
|
|
86988
|
+
const body2 = normalizeInline2(renderChildren(nodes, { ...ctx, mode: "inline" }));
|
|
86989
|
+
const label2 = body2 || attrs.name || attrs.title || attrs.label || attrs.value;
|
|
86936
86990
|
const details = meaningfulAttributes(attrs, new Set(["name", "title", "label", "value"])).map(([key, value]) => `${key}=${value}`);
|
|
86937
86991
|
if (label2 === undefined && details.length === 0) {
|
|
86938
86992
|
ctx.tracker.flag(blockType, `${blockType} has no visible label or metadata`, "warning", "lark.capture.poll-option-empty", "projection");
|
|
@@ -86943,10 +86997,10 @@ function renderPollOption(blockType, nodes, attrs, ctx) {
|
|
|
86943
86997
|
`;
|
|
86944
86998
|
}
|
|
86945
86999
|
function renderPoll(nodes, attrs, ctx) {
|
|
86946
|
-
const
|
|
86947
|
-
const resource = registerLarkResource(ctx, "poll", "poll", attrs,
|
|
87000
|
+
const title2 = attrs.name ?? attrs.title ?? "Untitled poll";
|
|
87001
|
+
const resource = registerLarkResource(ctx, "poll", "poll", attrs, title2);
|
|
86948
87002
|
const href = attrs.href ?? attrs.url;
|
|
86949
|
-
const label2 = href !== undefined && !isTransientLarkMediaUrl(href) ? `[${escapeMarkdownLabel2(
|
|
87003
|
+
const label2 = href !== undefined && !isTransientLarkMediaUrl(href) ? `[${escapeMarkdownLabel2(title2)}](${href})` : escapeMarkdownLabel2(title2);
|
|
86950
87004
|
const details = meaningfulAttributes(attrs, new Set(["name", "title", "href", "url"])).map(([key, value]) => `${key}=${value}`);
|
|
86951
87005
|
const children = normalizeMarkdown(renderChildren(nodes, { ...ctx, mode: "block" }));
|
|
86952
87006
|
resource.inline_content = children.length > 0;
|
|
@@ -86963,15 +87017,15 @@ ${lines.join(`
|
|
|
86963
87017
|
`;
|
|
86964
87018
|
}
|
|
86965
87019
|
function renderUnknown(name3, nodes, attrs, ctx) {
|
|
86966
|
-
const
|
|
87020
|
+
const body2 = normalizeMarkdown(renderChildren(nodes, ctx));
|
|
86967
87021
|
const exportedAttrs = auditableAttributes(attrs);
|
|
86968
|
-
if (
|
|
87022
|
+
if (body2.length === 0 && exportedAttrs.length === 0) {
|
|
86969
87023
|
ctx.tracker.skip(name3, "unknown empty block omitted", "warning");
|
|
86970
87024
|
return "";
|
|
86971
87025
|
}
|
|
86972
87026
|
ctx.tracker.convert(name3);
|
|
86973
87027
|
ctx.tracker.flag(name3, "block was preserved through the generic non-interactive projection; inspect source.xml for the original structure", "warning", "lark.capture.generic-projection", "projection");
|
|
86974
|
-
const digest6 =
|
|
87028
|
+
const digest6 = createHash26("sha256").update(JSON.stringify({ name: name3, attributes: exportedAttrs, text: normalizeInline2(textContent2(nodes)) }), "utf8").digest("hex").slice(0, 12);
|
|
86975
87029
|
const locator = `lark:block:${name3}:${digest6}`;
|
|
86976
87030
|
ctx.resources.push({
|
|
86977
87031
|
kind: "embed",
|
|
@@ -86982,7 +87036,7 @@ function renderUnknown(name3, nodes, attrs, ctx) {
|
|
|
86982
87036
|
const lines = [
|
|
86983
87037
|
`> Lark block (generic projection): \`${name3}\` <!-- ${locator} -->`,
|
|
86984
87038
|
...exportedAttrs.length > 0 ? [`> Exported attributes: ${JSON.stringify(Object.fromEntries(exportedAttrs))}`] : [],
|
|
86985
|
-
...
|
|
87039
|
+
...body2.length > 0 ? [body2] : []
|
|
86986
87040
|
];
|
|
86987
87041
|
return `
|
|
86988
87042
|
|
|
@@ -87031,10 +87085,10 @@ ${"#".repeat(Math.min(Math.max(level, 1), 6))} ${normalizeInline2(renderChildren
|
|
|
87031
87085
|
}
|
|
87032
87086
|
if (["p", "paragraph", "div", "section"].includes(name3)) {
|
|
87033
87087
|
ctx.tracker.convert(name3);
|
|
87034
|
-
const
|
|
87035
|
-
return ctx.mode === "inline" ?
|
|
87088
|
+
const body2 = renderChildren(nodes, { ...ctx, mode: "inline" });
|
|
87089
|
+
return ctx.mode === "inline" ? body2 : `
|
|
87036
87090
|
|
|
87037
|
-
${
|
|
87091
|
+
${body2}
|
|
87038
87092
|
|
|
87039
87093
|
`;
|
|
87040
87094
|
}
|
|
@@ -87074,8 +87128,8 @@ ${body}
|
|
|
87074
87128
|
}
|
|
87075
87129
|
if (name3 === "code") {
|
|
87076
87130
|
ctx.tracker.convert(name3);
|
|
87077
|
-
const
|
|
87078
|
-
return ctx.mode === "code" ?
|
|
87131
|
+
const body2 = renderChildren(nodes, { ...ctx, mode: "code" });
|
|
87132
|
+
return ctx.mode === "code" ? body2 : `\`${body2.replace(/`/gu, "\\`")}\``;
|
|
87079
87133
|
}
|
|
87080
87134
|
if (name3 === "pre") {
|
|
87081
87135
|
ctx.tracker.convert(name3);
|
|
@@ -87127,10 +87181,10 @@ ${renderTable(nodes, ctx)}
|
|
|
87127
87181
|
if (name3 === "callout" || name3 === "blockquote" || name3 === "quote") {
|
|
87128
87182
|
ctx.tracker.convert(name3);
|
|
87129
87183
|
const prefix = attrs.emoji === undefined ? "" : `${attrs.emoji} `;
|
|
87130
|
-
const
|
|
87184
|
+
const body2 = normalizeMarkdown(renderChildren(nodes, { ...ctx, mode: "block" }));
|
|
87131
87185
|
return `
|
|
87132
87186
|
|
|
87133
|
-
${
|
|
87187
|
+
${body2.split(`
|
|
87134
87188
|
`).map((line, index2) => `> ${index2 === 0 ? prefix : ""}${line}`).join(`
|
|
87135
87189
|
`)}
|
|
87136
87190
|
|
|
@@ -87186,10 +87240,10 @@ function projectLarkDocxXml(input) {
|
|
|
87186
87240
|
mode: "block"
|
|
87187
87241
|
}));
|
|
87188
87242
|
const titleNode = rootChildren.find((node3) => elementName2(node3) === "title");
|
|
87189
|
-
const
|
|
87243
|
+
const title2 = titleNode === undefined ? undefined : normalizeInline2(textContent2(elementChildren2(titleNode, "title")));
|
|
87190
87244
|
return {
|
|
87191
87245
|
markdown,
|
|
87192
|
-
...
|
|
87246
|
+
...title2 !== undefined && title2.length > 0 ? { title: title2 } : {},
|
|
87193
87247
|
auditXml: sanitizeAuditXml(input.xml),
|
|
87194
87248
|
rawContentHash,
|
|
87195
87249
|
resources,
|
|
@@ -87262,6 +87316,32 @@ var init_larkDocxXml = __esm(() => {
|
|
|
87262
87316
|
]);
|
|
87263
87317
|
});
|
|
87264
87318
|
|
|
87319
|
+
// src/lib/larkImagePolicy.ts
|
|
87320
|
+
function omitLarkImage(resource, policy, items, replacements, mediaType, failure2) {
|
|
87321
|
+
if (resource.kind !== "image" && !mediaType?.startsWith("image/"))
|
|
87322
|
+
return false;
|
|
87323
|
+
const gif = mediaType === "image/gif" || /\.gif$/iu.test(resource.title ?? "") || Object.entries(resource.attributes).some(([key, value]) => ["mime_type", "content_type"].includes(key) && value === "image/gif");
|
|
87324
|
+
if (!failure2 && policy.images !== "reference-only" && !(gif && policy.gifs === "reference-only"))
|
|
87325
|
+
return false;
|
|
87326
|
+
const title2 = (resource.title ?? "image").replace(/[\r\n<>]/gu, " ");
|
|
87327
|
+
replacements.set(resource.locator, `> Image omitted: ${title2}. ${failure2 ? "Resource size limit exceeded." : "Excluded by selected policy."} See the source document. <!-- ${resource.locator} -->`);
|
|
87328
|
+
items.push({
|
|
87329
|
+
kind: resource.kind,
|
|
87330
|
+
locator: resource.locator,
|
|
87331
|
+
status: "reference-only",
|
|
87332
|
+
required: false,
|
|
87333
|
+
asset_paths: [],
|
|
87334
|
+
reason_code: failure2 ? "image-budget-exceeded" : "image-excluded-by-policy",
|
|
87335
|
+
reason: failure2 ?? "Image bytes were not retained; the selected image policy preserves a placeholder and source reference"
|
|
87336
|
+
});
|
|
87337
|
+
return true;
|
|
87338
|
+
}
|
|
87339
|
+
var LarkResourceBudgetError;
|
|
87340
|
+
var init_larkImagePolicy = __esm(() => {
|
|
87341
|
+
LarkResourceBudgetError = class LarkResourceBudgetError extends Error {
|
|
87342
|
+
};
|
|
87343
|
+
});
|
|
87344
|
+
|
|
87265
87345
|
// src/lib/larkResourceCommand.ts
|
|
87266
87346
|
function stableJson(value) {
|
|
87267
87347
|
if (value === undefined)
|
|
@@ -87331,9 +87411,9 @@ var init_larkResourceCommand = __esm(() => {
|
|
|
87331
87411
|
});
|
|
87332
87412
|
|
|
87333
87413
|
// src/lib/larkResourceMaterialization.ts
|
|
87334
|
-
import { createHash as
|
|
87335
|
-
import { mkdtemp as mkdtemp4, readFile as
|
|
87336
|
-
import { extname as extname13, join as
|
|
87414
|
+
import { createHash as createHash27 } from "node:crypto";
|
|
87415
|
+
import { mkdtemp as mkdtemp4, readFile as readFile71, readdir as readdir21, rm as rm19 } from "node:fs/promises";
|
|
87416
|
+
import { extname as extname13, join as join91 } from "node:path";
|
|
87337
87417
|
import { tmpdir } from "node:os";
|
|
87338
87418
|
function countByKind(items, status) {
|
|
87339
87419
|
const counts2 = new Map;
|
|
@@ -87345,7 +87425,7 @@ function countByKind(items, status) {
|
|
|
87345
87425
|
return Object.fromEntries([...counts2].sort(([left], [right]) => left.localeCompare(right)));
|
|
87346
87426
|
}
|
|
87347
87427
|
function resourceDigest(resource) {
|
|
87348
|
-
return
|
|
87428
|
+
return createHash27("sha256").update(`${resource.kind}\x00${resource.locator}`, "utf8").digest("hex").slice(0, 20);
|
|
87349
87429
|
}
|
|
87350
87430
|
function safeLabel(value, fallback) {
|
|
87351
87431
|
const normalized = (value ?? fallback).replace(/[\r\n]+/gu, " ").trim();
|
|
@@ -87438,10 +87518,10 @@ function findBooleanField(value, name3) {
|
|
|
87438
87518
|
}
|
|
87439
87519
|
async function downloadedFile(input) {
|
|
87440
87520
|
if (input.localPath !== undefined) {
|
|
87441
|
-
const bytes = await
|
|
87521
|
+
const bytes = await readFile71(input.localPath);
|
|
87442
87522
|
return { path: input.localPath, bytes, mediaType: mediaTypeFor(input.localPath, bytes) };
|
|
87443
87523
|
}
|
|
87444
|
-
const tempRoot = await mkdtemp4(
|
|
87524
|
+
const tempRoot = await mkdtemp4(join91(tmpdir(), "context-lark-resource-"));
|
|
87445
87525
|
try {
|
|
87446
87526
|
await runLarkResourceCommand(input.runner, [
|
|
87447
87527
|
"docs",
|
|
@@ -87458,11 +87538,11 @@ async function downloadedFile(input) {
|
|
|
87458
87538
|
"--format",
|
|
87459
87539
|
"json"
|
|
87460
87540
|
], { cwd: tempRoot });
|
|
87461
|
-
const entries2 = (await
|
|
87541
|
+
const entries2 = (await readdir21(tempRoot, { withFileTypes: true })).filter((entry) => entry.isFile());
|
|
87462
87542
|
if (entries2.length !== 1)
|
|
87463
87543
|
throw new Error(`media download produced ${entries2.length} files, expected exactly one`);
|
|
87464
87544
|
const path3 = entries2[0]?.name ?? "resource.bin";
|
|
87465
|
-
const bytes = await
|
|
87545
|
+
const bytes = await readFile71(join91(tempRoot, path3));
|
|
87466
87546
|
return { path: path3, bytes, mediaType: mediaTypeFor(path3, bytes) };
|
|
87467
87547
|
} finally {
|
|
87468
87548
|
await rm19(tempRoot, { recursive: true, force: true });
|
|
@@ -87553,9 +87633,9 @@ async function sheetMaterialization(resource, runner2, identity) {
|
|
|
87553
87633
|
const sheetId = resource.attributes["sheet-id"];
|
|
87554
87634
|
if (token === undefined || sheetId === undefined)
|
|
87555
87635
|
throw new Error("embedded Sheet requires token and sheet-id");
|
|
87556
|
-
const tempRoot = await mkdtemp4(
|
|
87636
|
+
const tempRoot = await mkdtemp4(join91(tmpdir(), "context-lark-sheet-"));
|
|
87557
87637
|
try {
|
|
87558
|
-
const outputPath =
|
|
87638
|
+
const outputPath = join91(tempRoot, "sheet.json");
|
|
87559
87639
|
const stdout = await runLarkResourceCommand(runner2, [
|
|
87560
87640
|
"sheets",
|
|
87561
87641
|
"+csv-get",
|
|
@@ -87575,13 +87655,13 @@ async function sheetMaterialization(resource, runner2, identity) {
|
|
|
87575
87655
|
if (findBooleanField(receipt2, "truncated") === true || findBooleanField(receipt2, "complete") === false) {
|
|
87576
87656
|
throw new Error("embedded Sheet read was truncated");
|
|
87577
87657
|
}
|
|
87578
|
-
const payload = JSON.parse(await
|
|
87658
|
+
const payload = JSON.parse(await readFile71(outputPath, "utf8"));
|
|
87579
87659
|
const csv = findStringField(payload, new Set(["annotated_csv", "csv", "content", "text"]));
|
|
87580
87660
|
if (csv === undefined)
|
|
87581
87661
|
throw new Error("embedded Sheet response has no CSV payload");
|
|
87582
87662
|
const digest6 = resourceDigest(resource);
|
|
87583
87663
|
const path3 = `materialized/sheet/${digest6}.csv`;
|
|
87584
|
-
const
|
|
87664
|
+
const title2 = safeLabel(resource.title, "Embedded Sheet");
|
|
87585
87665
|
return {
|
|
87586
87666
|
asset: {
|
|
87587
87667
|
path: path3,
|
|
@@ -87590,7 +87670,7 @@ async function sheetMaterialization(resource, runner2, identity) {
|
|
|
87590
87670
|
role: "evidence",
|
|
87591
87671
|
source: { kind: resource.kind, locator: resource.locator }
|
|
87592
87672
|
},
|
|
87593
|
-
replacement: `#### ${
|
|
87673
|
+
replacement: `#### ${title2}
|
|
87594
87674
|
|
|
87595
87675
|
${markdownTable2(parseCsv(csv))}
|
|
87596
87676
|
|
|
@@ -87690,7 +87770,7 @@ async function baseMaterialization(resource, runner2, identity) {
|
|
|
87690
87770
|
`;
|
|
87691
87771
|
const digest6 = resourceDigest(resource);
|
|
87692
87772
|
const path3 = `materialized/base/${digest6}.json`;
|
|
87693
|
-
const
|
|
87773
|
+
const title2 = safeLabel(resource.title, "Embedded Base");
|
|
87694
87774
|
return {
|
|
87695
87775
|
asset: {
|
|
87696
87776
|
path: path3,
|
|
@@ -87699,7 +87779,7 @@ async function baseMaterialization(resource, runner2, identity) {
|
|
|
87699
87779
|
role: "evidence",
|
|
87700
87780
|
source: { kind: resource.kind, locator: resource.locator }
|
|
87701
87781
|
},
|
|
87702
|
-
replacement: `#### ${
|
|
87782
|
+
replacement: `#### ${title2}
|
|
87703
87783
|
|
|
87704
87784
|
${markdownTable2(baseRows(records, fieldOrder))}
|
|
87705
87785
|
|
|
@@ -87711,7 +87791,7 @@ async function whiteboardMaterialization(resource, runner2, identity) {
|
|
|
87711
87791
|
if (token === undefined)
|
|
87712
87792
|
throw new Error(`${resource.kind} has no whiteboard token`);
|
|
87713
87793
|
const preview = await downloadedFile({ runner: runner2, identity, token, type: "whiteboard" });
|
|
87714
|
-
const tempRoot = await mkdtemp4(
|
|
87794
|
+
const tempRoot = await mkdtemp4(join91(tmpdir(), "context-lark-whiteboard-"));
|
|
87715
87795
|
let rawPayload;
|
|
87716
87796
|
try {
|
|
87717
87797
|
await runLarkResourceCommand(runner2, [
|
|
@@ -87729,14 +87809,14 @@ async function whiteboardMaterialization(resource, runner2, identity) {
|
|
|
87729
87809
|
"--format",
|
|
87730
87810
|
"json"
|
|
87731
87811
|
], { cwd: tempRoot });
|
|
87732
|
-
rawPayload = JSON.parse(await
|
|
87812
|
+
rawPayload = JSON.parse(await readFile71(join91(tempRoot, "raw.json"), "utf8"));
|
|
87733
87813
|
} finally {
|
|
87734
87814
|
await rm19(tempRoot, { recursive: true, force: true });
|
|
87735
87815
|
}
|
|
87736
87816
|
const digest6 = resourceDigest(resource);
|
|
87737
87817
|
const previewPath = `materialized/${resource.kind}/${digest6}${extensionFor2(preview.mediaType, preview.path)}`;
|
|
87738
87818
|
const rawPath = `materialized/${resource.kind}/${digest6}.json`;
|
|
87739
|
-
const
|
|
87819
|
+
const title2 = markdownLabel(safeLabel(resource.title, resource.kind === "diagram" ? "Diagram" : "Whiteboard"));
|
|
87740
87820
|
return {
|
|
87741
87821
|
assets: [
|
|
87742
87822
|
{
|
|
@@ -87755,20 +87835,20 @@ async function whiteboardMaterialization(resource, runner2, identity) {
|
|
|
87755
87835
|
source: { kind: resource.kind, locator: resource.locator }
|
|
87756
87836
|
}
|
|
87757
87837
|
],
|
|
87758
|
-
replacement: `})
|
|
87759
87839
|
|
|
87760
87840
|
[Raw snapshot](${sourceAssetTarget(rawPath)}) <!-- ${resource.locator} -->`
|
|
87761
87841
|
};
|
|
87762
87842
|
}
|
|
87763
87843
|
function placeholderFor(resource) {
|
|
87764
|
-
const
|
|
87844
|
+
const title2 = safeLabel(resource.title, resource.kind);
|
|
87765
87845
|
switch (resource.kind) {
|
|
87766
87846
|
case "image":
|
|
87767
|
-
return `> Image: ${
|
|
87847
|
+
return `> Image: ${title2} (${resource.locator})`;
|
|
87768
87848
|
case "video":
|
|
87769
|
-
return `> Video: ${
|
|
87849
|
+
return `> Video: ${title2} (${resource.locator})`;
|
|
87770
87850
|
case "file":
|
|
87771
|
-
return `> File: ${
|
|
87851
|
+
return `> File: ${title2} (${resource.locator})`;
|
|
87772
87852
|
case "whiteboard":
|
|
87773
87853
|
return `> Whiteboard: ${resource.locator}`;
|
|
87774
87854
|
case "diagram":
|
|
@@ -87790,7 +87870,7 @@ function referenceOnlyReason(resource) {
|
|
|
87790
87870
|
}
|
|
87791
87871
|
function materializationReport(items) {
|
|
87792
87872
|
const hasRequiredFailure = items.some((item) => item.status === "failed" && item.required && !isNonBlockingDocumentResourceFailureReasonCode(item.reason_code));
|
|
87793
|
-
const hasOptionalFailure = items.some((item) => item.status === "failed") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true);
|
|
87873
|
+
const hasOptionalFailure = items.some((item) => item.status === "failed" || item.reason_code === "image-budget-exceeded") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true);
|
|
87794
87874
|
return {
|
|
87795
87875
|
status: hasRequiredFailure ? "error" : hasOptionalFailure ? "warning" : "complete",
|
|
87796
87876
|
discovered: countByKind(items),
|
|
@@ -87810,16 +87890,16 @@ function resourceFailureReasonCode(resource, error) {
|
|
|
87810
87890
|
return /\b2890003\b/u.test(message) ? DOCUMENT_RESOURCE_SOURCE_MISSING_REASON_CODE : undefined;
|
|
87811
87891
|
}
|
|
87812
87892
|
function unavailableReplacement(resource, reasonCode) {
|
|
87813
|
-
const
|
|
87893
|
+
const title2 = markdownLabel(safeLabel(resource.title, resource.kind));
|
|
87814
87894
|
const reason = reasonCode === DOCUMENT_RESOURCE_PERMISSION_DENIED_REASON_CODE ? "export permission denied" : "source no longer exists";
|
|
87815
|
-
return `> Resource unavailable: ${
|
|
87895
|
+
return `> Resource unavailable: ${title2} (${resource.kind}; ${reason}). <!-- ${resource.locator} -->`;
|
|
87816
87896
|
}
|
|
87817
87897
|
function assertBudget(asset, policy, total) {
|
|
87818
87898
|
if (asset.bytes.byteLength > policy.maxBytesPerResource) {
|
|
87819
|
-
throw new
|
|
87899
|
+
throw new LarkResourceBudgetError(`resource is ${asset.bytes.byteLength} bytes, above maxBytesPerResource=${policy.maxBytesPerResource}`);
|
|
87820
87900
|
}
|
|
87821
87901
|
if (total + asset.bytes.byteLength > policy.maxTotalBytes) {
|
|
87822
|
-
throw new
|
|
87902
|
+
throw new LarkResourceBudgetError(`materialized resources exceed maxTotalBytes=${policy.maxTotalBytes}`);
|
|
87823
87903
|
}
|
|
87824
87904
|
}
|
|
87825
87905
|
async function materializeLarkResources(input) {
|
|
@@ -87835,6 +87915,8 @@ async function materializeLarkResources(input) {
|
|
|
87835
87915
|
return;
|
|
87836
87916
|
seen.add(key);
|
|
87837
87917
|
const required = REQUIRED_KINDS.has(resource.kind);
|
|
87918
|
+
if (omitLarkImage(resource, input.policy, items, replacements))
|
|
87919
|
+
return;
|
|
87838
87920
|
try {
|
|
87839
87921
|
if (resource.kind === "diagram" && resource.inline_content === true) {
|
|
87840
87922
|
replacements.set(resource.locator, "");
|
|
@@ -87921,6 +88003,8 @@ async function materializeLarkResources(input) {
|
|
|
87921
88003
|
type: "media",
|
|
87922
88004
|
...input.mediaFiles?.[token] === undefined ? {} : { localPath: input.mediaFiles[token] }
|
|
87923
88005
|
});
|
|
88006
|
+
if (omitLarkImage(resource, input.policy, items, replacements, downloaded.mediaType))
|
|
88007
|
+
return;
|
|
87924
88008
|
const digest6 = resourceDigest(resource);
|
|
87925
88009
|
const extension2 = extensionFor2(downloaded.mediaType, downloaded.path);
|
|
87926
88010
|
const asset = {
|
|
@@ -87933,12 +88017,14 @@ async function materializeLarkResources(input) {
|
|
|
87933
88017
|
assertBudget(asset, input.policy, totalBytes);
|
|
87934
88018
|
totalBytes += asset.bytes.byteLength;
|
|
87935
88019
|
assets.push(asset);
|
|
87936
|
-
const
|
|
88020
|
+
const title2 = markdownLabel(safeLabel(resource.title, resource.kind));
|
|
87937
88021
|
const target = sourceAssetTarget(asset.path);
|
|
87938
|
-
const replacement = downloaded.mediaType.startsWith("image/") ? ` <!-- ${resource.locator} -->` : `[${title2}](${target}) <!-- ${resource.locator} -->`;
|
|
87939
88023
|
replacements.set(resource.locator, replacement);
|
|
87940
88024
|
items.push({ kind: resource.kind, locator: resource.locator, status: "materialized", required, asset_paths: [asset.path] });
|
|
87941
88025
|
} catch (error) {
|
|
88026
|
+
if (error instanceof LarkResourceBudgetError && omitLarkImage(resource, input.policy, items, replacements, undefined, error.message))
|
|
88027
|
+
return;
|
|
87942
88028
|
const reasonCode = resourceFailureReasonCode(resource, error);
|
|
87943
88029
|
if (isNonBlockingDocumentResourceFailureReasonCode(reasonCode)) {
|
|
87944
88030
|
replacements.set(resource.locator, unavailableReplacement(resource, reasonCode));
|
|
@@ -87983,6 +88069,7 @@ function applyLarkResourceReplacements(markdown, resources, replacements) {
|
|
|
87983
88069
|
}
|
|
87984
88070
|
var REQUIRED_KINDS, MEDIA_TYPES_BY_EXTENSION;
|
|
87985
88071
|
var init_larkResourceMaterialization = __esm(() => {
|
|
88072
|
+
init_larkImagePolicy();
|
|
87986
88073
|
init_src3();
|
|
87987
88074
|
init_larkResourceCommand();
|
|
87988
88075
|
REQUIRED_KINDS = new Set([
|
|
@@ -88155,9 +88242,9 @@ function payloadShapeSummary(payload) {
|
|
|
88155
88242
|
}
|
|
88156
88243
|
function extractDocsFetchContent(payload, requestedFormat) {
|
|
88157
88244
|
const document4 = payload.document && typeof payload.document === "object" ? payload.document : undefined;
|
|
88158
|
-
const
|
|
88245
|
+
const title2 = stringValue(payload.title) ?? stringValue(document4?.title);
|
|
88159
88246
|
const markdown = stringValue(payload.markdown) ?? stringValue(document4?.markdown);
|
|
88160
|
-
const withTitle = (result) =>
|
|
88247
|
+
const withTitle = (result) => title2 === undefined ? result : { ...result, title: title2 };
|
|
88161
88248
|
if (markdown !== undefined) {
|
|
88162
88249
|
if (requestedFormat === "xml") {
|
|
88163
88250
|
throw new LarkCliError(`${LARK_BIN} docs +fetch returned Markdown despite --doc-format xml; capture stopped because the response cannot provide auditable rich-block fidelity. Upgrade lark-cli and retry.`, 0, "");
|
|
@@ -88275,7 +88362,7 @@ function userIdentityUnavailable(error) {
|
|
|
88275
88362
|
async function fetchDocsResponse(input, docsFetchPlan, runner2, identity) {
|
|
88276
88363
|
const chunks = [];
|
|
88277
88364
|
let contentFormat;
|
|
88278
|
-
let
|
|
88365
|
+
let title2;
|
|
88279
88366
|
let revisionId;
|
|
88280
88367
|
let unsupportedShape;
|
|
88281
88368
|
const assets = [];
|
|
@@ -88305,7 +88392,7 @@ async function fetchDocsResponse(input, docsFetchPlan, runner2, identity) {
|
|
|
88305
88392
|
}
|
|
88306
88393
|
contentFormat = extracted.format;
|
|
88307
88394
|
if (page === 0 && extracted.title !== undefined)
|
|
88308
|
-
|
|
88395
|
+
title2 = extracted.title;
|
|
88309
88396
|
revisionId ??= extractDocsFetchRevisionId(payload);
|
|
88310
88397
|
assets.push(...extractDocsFetchAssets(payload));
|
|
88311
88398
|
if (extracted.body !== undefined && extracted.body.length > 0) {
|
|
@@ -88327,19 +88414,19 @@ async function fetchDocsResponse(input, docsFetchPlan, runner2, identity) {
|
|
|
88327
88414
|
throw new LarkCliError(`${LARK_BIN} docs +fetch exceeded ${MAX_FETCH_PAGES} pagination calls; likely a server-side issue`, 0, "");
|
|
88328
88415
|
}
|
|
88329
88416
|
}
|
|
88330
|
-
const
|
|
88417
|
+
const body2 = chunks.join(`
|
|
88331
88418
|
|
|
88332
88419
|
`);
|
|
88333
|
-
if (
|
|
88420
|
+
if (body2.trim().length === 0 && (title2 === undefined || title2.length === 0)) {
|
|
88334
88421
|
if (unsupportedShape !== undefined) {
|
|
88335
88422
|
throw new LarkCliError(`${LARK_BIN} docs +fetch returned an unsupported payload shape (${unsupportedShape}). Expected data.markdown or data.document.content; this is a format adapter issue, not a permission error.`, 0, "");
|
|
88336
88423
|
}
|
|
88337
88424
|
throw new LarkCliError("document is empty — it may not exist or you lack permission", 0, "");
|
|
88338
88425
|
}
|
|
88339
88426
|
return {
|
|
88340
|
-
body,
|
|
88427
|
+
body: body2,
|
|
88341
88428
|
contentFormat,
|
|
88342
|
-
...
|
|
88429
|
+
...title2 !== undefined ? { title: title2 } : {},
|
|
88343
88430
|
...revisionId !== undefined ? { revisionId } : {},
|
|
88344
88431
|
assets
|
|
88345
88432
|
};
|
|
@@ -88392,8 +88479,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88392
88479
|
break;
|
|
88393
88480
|
fetched = await fetchDocsResponse(input, docsFetchPlan, runner2, accessIdentity);
|
|
88394
88481
|
}
|
|
88395
|
-
let
|
|
88396
|
-
let
|
|
88482
|
+
let body2 = fetched.body;
|
|
88483
|
+
let title2 = fetched.title;
|
|
88397
88484
|
const revisionId = fetched.revisionId;
|
|
88398
88485
|
const assets = [...fetched.assets];
|
|
88399
88486
|
let fidelity = emptyFidelityReport();
|
|
@@ -88406,8 +88493,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88406
88493
|
items: []
|
|
88407
88494
|
};
|
|
88408
88495
|
if (projection !== undefined) {
|
|
88409
|
-
|
|
88410
|
-
|
|
88496
|
+
body2 = projection.markdown;
|
|
88497
|
+
title2 ??= projection.title;
|
|
88411
88498
|
fidelity = projection.fidelity;
|
|
88412
88499
|
assets.push({
|
|
88413
88500
|
path: "source.xml",
|
|
@@ -88420,7 +88507,7 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88420
88507
|
}
|
|
88421
88508
|
});
|
|
88422
88509
|
}
|
|
88423
|
-
const resources = [...new Map([...projection?.resources ?? [], ...larkMarkdownImageResources(
|
|
88510
|
+
const resources = [...new Map([...projection?.resources ?? [], ...larkMarkdownImageResources(body2)].map((resource) => [resource.locator, resource])).values()];
|
|
88424
88511
|
if (resources.length) {
|
|
88425
88512
|
const policy = {
|
|
88426
88513
|
...DEFAULT_RESOURCE_POLICY,
|
|
@@ -88449,8 +88536,8 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88449
88536
|
});
|
|
88450
88537
|
}
|
|
88451
88538
|
});
|
|
88452
|
-
|
|
88453
|
-
|
|
88539
|
+
body2 = applyLarkResourceReplacements(body2, resources, materialized.replacements);
|
|
88540
|
+
body2 = replaceLarkMarkdownImages(body2, materialized.replacements);
|
|
88454
88541
|
assets.push(...materialized.assets.map((asset) => ({
|
|
88455
88542
|
path: asset.path,
|
|
88456
88543
|
bytes: asset.bytes,
|
|
@@ -88465,12 +88552,12 @@ async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
|
88465
88552
|
resourceMaterialization,
|
|
88466
88553
|
resources
|
|
88467
88554
|
}));
|
|
88468
|
-
if (
|
|
88555
|
+
if (title2 !== undefined && title2.length > 0 && !/^#\s/.test(body2)) {
|
|
88469
88556
|
return {
|
|
88470
|
-
markdown: `# ${
|
|
88557
|
+
markdown: `# ${title2}
|
|
88471
88558
|
|
|
88472
|
-
${
|
|
88473
|
-
title,
|
|
88559
|
+
${body2}`,
|
|
88560
|
+
title: title2,
|
|
88474
88561
|
...revisionId !== undefined ? { revisionId } : {},
|
|
88475
88562
|
assets,
|
|
88476
88563
|
fidelity,
|
|
@@ -88480,8 +88567,8 @@ ${body}`,
|
|
|
88480
88567
|
};
|
|
88481
88568
|
}
|
|
88482
88569
|
return {
|
|
88483
|
-
markdown:
|
|
88484
|
-
...
|
|
88570
|
+
markdown: body2,
|
|
88571
|
+
...title2 !== undefined ? { title: title2 } : {},
|
|
88485
88572
|
...revisionId !== undefined ? { revisionId } : {},
|
|
88486
88573
|
assets,
|
|
88487
88574
|
fidelity,
|
|
@@ -88574,8 +88661,8 @@ var init_sensitiveSourceLiteral = __esm(() => {
|
|
|
88574
88661
|
var LARK_DOCUMENT_NORMALIZER_VERSION = "lark-document-normalizer.v1";
|
|
88575
88662
|
|
|
88576
88663
|
// src/project/documentCaptureLark.ts
|
|
88577
|
-
import { readdir as
|
|
88578
|
-
import { basename as basename10, extname as extname14, join as
|
|
88664
|
+
import { readdir as readdir22, readFile as readFile72 } from "node:fs/promises";
|
|
88665
|
+
import { basename as basename10, extname as extname14, join as join92 } from "node:path";
|
|
88579
88666
|
function titleFromMarkdown2(markdown, fallbackPath) {
|
|
88580
88667
|
const heading2 = markdown.split(`
|
|
88581
88668
|
`).find((line) => /^#\s+\S/u.test(line));
|
|
@@ -88594,7 +88681,7 @@ function countLines3(markdown) {
|
|
|
88594
88681
|
}
|
|
88595
88682
|
async function fileContentMatches(path3, content3) {
|
|
88596
88683
|
try {
|
|
88597
|
-
const current2 = await
|
|
88684
|
+
const current2 = await readFile72(path3);
|
|
88598
88685
|
const expected = typeof content3 === "string" ? Buffer.from(content3, "utf8") : Buffer.from(content3);
|
|
88599
88686
|
return current2.equals(expected);
|
|
88600
88687
|
} catch {
|
|
@@ -88602,7 +88689,7 @@ async function fileContentMatches(path3, content3) {
|
|
|
88602
88689
|
}
|
|
88603
88690
|
}
|
|
88604
88691
|
function sourceManifestPath2(entry) {
|
|
88605
|
-
return entry.snapshot?.manifest ??
|
|
88692
|
+
return entry.snapshot?.manifest ?? join92(entry.materializedAt, "manifest.json");
|
|
88606
88693
|
}
|
|
88607
88694
|
function larkRuntimeError(message, detail) {
|
|
88608
88695
|
return new ContextError(ExitCode.ExternalToolError, message, {
|
|
@@ -88728,12 +88815,12 @@ function assetManifestEntry(asset, assetRoot) {
|
|
|
88728
88815
|
};
|
|
88729
88816
|
}
|
|
88730
88817
|
async function listSnapshotAssetFiles(root2, assetRoot) {
|
|
88731
|
-
const assetsRoot =
|
|
88818
|
+
const assetsRoot = join92(root2, assetRoot);
|
|
88732
88819
|
const files = [];
|
|
88733
88820
|
const visit4 = async (dir, prefix = assetRoot) => {
|
|
88734
88821
|
let entries2;
|
|
88735
88822
|
try {
|
|
88736
|
-
entries2 = await
|
|
88823
|
+
entries2 = await readdir22(dir, { withFileTypes: true });
|
|
88737
88824
|
} catch (error) {
|
|
88738
88825
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
88739
88826
|
return;
|
|
@@ -88741,7 +88828,7 @@ async function listSnapshotAssetFiles(root2, assetRoot) {
|
|
|
88741
88828
|
}
|
|
88742
88829
|
for (const entry of entries2) {
|
|
88743
88830
|
const relPath = `${prefix}/${entry.name}`;
|
|
88744
|
-
const absolutePath =
|
|
88831
|
+
const absolutePath = join92(dir, entry.name);
|
|
88745
88832
|
if (entry.isDirectory()) {
|
|
88746
88833
|
await visit4(absolutePath, relPath);
|
|
88747
88834
|
continue;
|
|
@@ -88756,7 +88843,7 @@ async function listSnapshotAssetFiles(root2, assetRoot) {
|
|
|
88756
88843
|
}
|
|
88757
88844
|
async function staleSnapshotAssetPaths(input) {
|
|
88758
88845
|
const existingPaths = await listSnapshotAssetFiles(input.materializedAtAbsPath, input.assetRoot);
|
|
88759
|
-
return existingPaths.filter((path3) => !input.currentPaths.has(path3)).map((path3) =>
|
|
88846
|
+
return existingPaths.filter((path3) => !input.currentPaths.has(path3)).map((path3) => join92(input.materializedAtAbsPath, path3));
|
|
88760
88847
|
}
|
|
88761
88848
|
function normalizeLarkError(error, sourceName) {
|
|
88762
88849
|
if (error instanceof ContextError)
|
|
@@ -88838,7 +88925,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88838
88925
|
});
|
|
88839
88926
|
}
|
|
88840
88927
|
const documentPath = normalizeSnapshotRelativePath(entry.module === undefined ? "index.md" : `${entry.module}.md`);
|
|
88841
|
-
const
|
|
88928
|
+
const title2 = entry.title ?? fetched.title ?? titleFromMarkdown2(normalized, documentPath);
|
|
88842
88929
|
const locator = target.kind === "url" ? target.value : `${target.kind}:${target.value}`;
|
|
88843
88930
|
const assetRoot = entry.module === undefined ? "assets" : `assets/${entry.module}`;
|
|
88844
88931
|
const reportPath = normalizeSnapshotRelativePath(`${assetRoot}/capture-report.json`);
|
|
@@ -88860,13 +88947,13 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88860
88947
|
const snapshotFiles = [{
|
|
88861
88948
|
path: documentPath,
|
|
88862
88949
|
bytes: normalized,
|
|
88863
|
-
title,
|
|
88950
|
+
title: title2,
|
|
88864
88951
|
locator
|
|
88865
88952
|
}];
|
|
88866
88953
|
const manifestPath = sourceManifestPath2(entry);
|
|
88867
|
-
const manifestAbsPath =
|
|
88954
|
+
const manifestAbsPath = join92(input.projectRoot, manifestPath);
|
|
88868
88955
|
const materializedAt = entry.materializedAt;
|
|
88869
|
-
const materializedAtAbsPath =
|
|
88956
|
+
const materializedAtAbsPath = join92(input.projectRoot, materializedAt);
|
|
88870
88957
|
const manifest = createDocumentSnapshotManifest({
|
|
88871
88958
|
sourceType: "lark",
|
|
88872
88959
|
sourceName: resolved.sourceName,
|
|
@@ -88900,13 +88987,13 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88900
88987
|
}));
|
|
88901
88988
|
try {
|
|
88902
88989
|
const requestedWrites = [{
|
|
88903
|
-
path:
|
|
88990
|
+
path: join92(materializedAtAbsPath, documentPath),
|
|
88904
88991
|
bytes: normalized
|
|
88905
88992
|
}];
|
|
88906
88993
|
for (const asset of assets) {
|
|
88907
88994
|
if (asset.bytes !== undefined) {
|
|
88908
88995
|
requestedWrites.push({
|
|
88909
|
-
path:
|
|
88996
|
+
path: join92(materializedAtAbsPath, asset.entry.path),
|
|
88910
88997
|
bytes: asset.bytes
|
|
88911
88998
|
});
|
|
88912
88999
|
}
|
|
@@ -88923,7 +89010,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88923
89010
|
currentPaths: new Set(assets.filter((asset) => asset.bytes !== undefined).map((asset) => asset.entry.path))
|
|
88924
89011
|
});
|
|
88925
89012
|
await applyAtomicFileBatch({
|
|
88926
|
-
transactionRoot:
|
|
89013
|
+
transactionRoot: join92(input.projectRoot, ".tmp", "context-runtime", "capture-transactions"),
|
|
88927
89014
|
writes,
|
|
88928
89015
|
removals
|
|
88929
89016
|
});
|
|
@@ -88983,7 +89070,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
88983
89070
|
},
|
|
88984
89071
|
documents: [{
|
|
88985
89072
|
path: documentPath,
|
|
88986
|
-
title,
|
|
89073
|
+
title: title2,
|
|
88987
89074
|
line_count: countLines3(normalized)
|
|
88988
89075
|
}],
|
|
88989
89076
|
assets: assets.map((asset) => ({
|
|
@@ -89026,7 +89113,7 @@ __export(exports_actionInputWorkspace, {
|
|
|
89026
89113
|
assertActionInputWorkspace: () => assertActionInputWorkspace
|
|
89027
89114
|
});
|
|
89028
89115
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
89029
|
-
import { dirname as
|
|
89116
|
+
import { dirname as dirname39, resolve as resolve31 } from "node:path";
|
|
89030
89117
|
function assertActionInputWorkspace(cwd, inputPath) {
|
|
89031
89118
|
if (inputPath === "-")
|
|
89032
89119
|
return;
|
|
@@ -89039,7 +89126,7 @@ function assertActionInputWorkspace(cwd, inputPath) {
|
|
|
89039
89126
|
} catch {
|
|
89040
89127
|
return;
|
|
89041
89128
|
}
|
|
89042
|
-
const owner = findContextProjectRoot(
|
|
89129
|
+
const owner = findContextProjectRoot(dirname39(file));
|
|
89043
89130
|
if (owner === null || realpathSync2(owner.projectRoot) === realpathSync2(current2.projectRoot))
|
|
89044
89131
|
return;
|
|
89045
89132
|
throw new ContextError(ExitCode.WorkspaceStateError, "The completion input belongs to a different Context workspace. Run the workflow CLI and read/write this task's .tmp files in the same intended workspace. No tasks were submitted.", {
|
|
@@ -89069,7 +89156,7 @@ __export(exports_articleRetirement, {
|
|
|
89069
89156
|
retireArticles: () => retireArticles,
|
|
89070
89157
|
articleRetirementSchema: () => articleRetirementSchema
|
|
89071
89158
|
});
|
|
89072
|
-
import { readFile as
|
|
89159
|
+
import { readFile as readFile78, readdir as readdir23 } from "node:fs/promises";
|
|
89073
89160
|
import { posix as posix10 } from "node:path";
|
|
89074
89161
|
function rebuildInput(revision) {
|
|
89075
89162
|
return JSON.stringify({ id: `retirement-${revision.slice(7)}`, operation: "rebuild", timing: "priority", targets: [] });
|
|
@@ -89090,7 +89177,7 @@ function invalid2(reason, message, details = {}) {
|
|
|
89090
89177
|
}
|
|
89091
89178
|
async function text9(root2, path3) {
|
|
89092
89179
|
try {
|
|
89093
|
-
return await
|
|
89180
|
+
return await readFile78(await safeProjectTarget(root2, path3), "utf8");
|
|
89094
89181
|
} catch (error) {
|
|
89095
89182
|
if (error.code === "ENOENT")
|
|
89096
89183
|
return;
|
|
@@ -89128,7 +89215,7 @@ async function retireArticles(input) {
|
|
|
89128
89215
|
invalid2("article-retirement-active-review", "Finish the active candidate/revision delivery before retiring approved pages.");
|
|
89129
89216
|
}
|
|
89130
89217
|
const before = await text9(input.projectRoot, "knowledge/structure.yaml");
|
|
89131
|
-
const structure = before === undefined ? {} :
|
|
89218
|
+
const structure = before === undefined ? {} : import_yaml42.default.parse(before);
|
|
89132
89219
|
const articles = validateArticleStructureEntries(structure.articles ?? []);
|
|
89133
89220
|
const byPath = new Map(articles.map((article) => [article.path, article]));
|
|
89134
89221
|
const selected = new Set(value.targets.map((target) => target.path));
|
|
@@ -89202,7 +89289,7 @@ async function retireArticles(input) {
|
|
|
89202
89289
|
}
|
|
89203
89290
|
async function inspectTemplates(directory2) {
|
|
89204
89291
|
const absolute = await safeProjectTarget(input.projectRoot, directory2);
|
|
89205
|
-
const entries2 = await
|
|
89292
|
+
const entries2 = await readdir23(absolute, { withFileTypes: true }).catch((error) => {
|
|
89206
89293
|
if (error.code === "ENOENT")
|
|
89207
89294
|
return [];
|
|
89208
89295
|
throw error;
|
|
@@ -89251,9 +89338,9 @@ async function retireArticles(input) {
|
|
|
89251
89338
|
return [target.replacement ? { ...group, target: { artifact_ref: byPath.get(target.replacement).article_id } } : group];
|
|
89252
89339
|
});
|
|
89253
89340
|
const updated = updateKnowledgeMap(map4, { expected_revision: map4.revision, upsert, remove: [] });
|
|
89254
|
-
change(KNOWLEDGE_MAP_PATH, await text9(input.projectRoot, KNOWLEDGE_MAP_PATH),
|
|
89341
|
+
change(KNOWLEDGE_MAP_PATH, await text9(input.projectRoot, KNOWLEDGE_MAP_PATH), import_yaml42.default.stringify(updated));
|
|
89255
89342
|
}
|
|
89256
|
-
change("knowledge/structure.yaml", before,
|
|
89343
|
+
change("knowledge/structure.yaml", before, import_yaml42.default.stringify({ ...structure, articles: articles.filter((article) => !selected.has(article.path)) }));
|
|
89257
89344
|
targets.sort((a, b) => a.path.localeCompare(b.path));
|
|
89258
89345
|
const revision = indexerProtocolDigest({ value, readDigests, targets });
|
|
89259
89346
|
const preview = {
|
|
@@ -89297,7 +89384,7 @@ async function retireArticles(input) {
|
|
|
89297
89384
|
};
|
|
89298
89385
|
});
|
|
89299
89386
|
}
|
|
89300
|
-
var
|
|
89387
|
+
var import_yaml42, articleRetirementSchema, next = "context status --format json";
|
|
89301
89388
|
var init_articleRetirement = __esm(() => {
|
|
89302
89389
|
init_zod();
|
|
89303
89390
|
init_src2();
|
|
@@ -89316,7 +89403,7 @@ var init_articleRetirement = __esm(() => {
|
|
|
89316
89403
|
init_durableSingleFileTransaction();
|
|
89317
89404
|
init_durableMultiFileTransaction();
|
|
89318
89405
|
init_writeLock();
|
|
89319
|
-
|
|
89406
|
+
import_yaml42 = __toESM(require_dist(), 1);
|
|
89320
89407
|
articleRetirementSchema = exports_external.object({
|
|
89321
89408
|
reason: exports_external.string().trim().min(1),
|
|
89322
89409
|
targets: exports_external.array(exports_external.object({ path: exports_external.string().min(1), replacement: exports_external.string().min(1).optional() }).strict()).min(1)
|
|
@@ -89336,14 +89423,14 @@ function schemaOutputFormat(value) {
|
|
|
89336
89423
|
}
|
|
89337
89424
|
function writeSchemaOutput(value, format2) {
|
|
89338
89425
|
process.stdout.write(format2 === "json" ? `${JSON.stringify(value, null, 2)}
|
|
89339
|
-
` :
|
|
89426
|
+
` : import_yaml43.default.stringify(value));
|
|
89340
89427
|
}
|
|
89341
|
-
var
|
|
89428
|
+
var import_yaml43;
|
|
89342
89429
|
var init_schemaOutput = __esm(() => {
|
|
89343
89430
|
init_errors3();
|
|
89344
89431
|
init_cliFeedback();
|
|
89345
89432
|
init_exitCode();
|
|
89346
|
-
|
|
89433
|
+
import_yaml43 = __toESM(require_dist(), 1);
|
|
89347
89434
|
});
|
|
89348
89435
|
|
|
89349
89436
|
// src/project/writeLockRecovery.ts
|
|
@@ -89352,9 +89439,9 @@ __export(exports_writeLockRecovery, {
|
|
|
89352
89439
|
recoverWriterLock: () => recoverWriterLock,
|
|
89353
89440
|
inspectWriterLock: () => inspectWriterLock
|
|
89354
89441
|
});
|
|
89355
|
-
import { lstat as lstat10, mkdir as mkdir33, readFile as
|
|
89356
|
-
import { join as
|
|
89357
|
-
import { createHash as
|
|
89442
|
+
import { lstat as lstat10, mkdir as mkdir33, readFile as readFile79, readdir as readdir24, rename as rename8, rmdir as rmdir2 } from "node:fs/promises";
|
|
89443
|
+
import { join as join97 } from "node:path";
|
|
89444
|
+
import { createHash as createHash29, randomUUID as randomUUID7 } from "node:crypto";
|
|
89358
89445
|
async function inspectWriterLock(root2) {
|
|
89359
89446
|
const path3 = await safeProjectTarget(root2, lockRelative);
|
|
89360
89447
|
let stat10;
|
|
@@ -89368,7 +89455,7 @@ async function inspectWriterLock(root2) {
|
|
|
89368
89455
|
if (!stat10.isDirectory() || stat10.isSymbolicLink())
|
|
89369
89456
|
throw new Error("Writer lock must be a real directory.");
|
|
89370
89457
|
const ownerPath = await safeProjectTarget(root2, `${lockRelative}/owner.json`);
|
|
89371
|
-
const bytes = await
|
|
89458
|
+
const bytes = await readFile79(ownerPath, "utf8");
|
|
89372
89459
|
const owner = JSON.parse(bytes);
|
|
89373
89460
|
if (owner.protocol !== "context.project-write-lock.v1" || !Number.isSafeInteger(owner.pid) || owner.pid <= 0) {
|
|
89374
89461
|
throw new Error("Writer lock owner is invalid; preserve the lock for diagnosis.");
|
|
@@ -89380,7 +89467,7 @@ async function inspectWriterLock(root2) {
|
|
|
89380
89467
|
const code3 = error.code;
|
|
89381
89468
|
processState = code3 === "ESRCH" ? "not-running" : code3 === "EPERM" ? "running" : "unknown";
|
|
89382
89469
|
}
|
|
89383
|
-
const digest6 = `sha256:${
|
|
89470
|
+
const digest6 = `sha256:${createHash29("sha256").update(`${stat10.dev}:${stat10.ino}:${stat10.birthtimeMs}:${bytes}`).digest("hex")}`;
|
|
89384
89471
|
return { ...owner, process_state: processState, digest: digest6, path: lockRelative };
|
|
89385
89472
|
}
|
|
89386
89473
|
async function recoverWriterLock(input) {
|
|
@@ -89400,19 +89487,19 @@ async function recoverWriterLock(input) {
|
|
|
89400
89487
|
};
|
|
89401
89488
|
if (input.plan_digest !== before.digest)
|
|
89402
89489
|
throw new Error("Writer lock changed; preview recovery again.");
|
|
89403
|
-
const path3 =
|
|
89404
|
-
const guard =
|
|
89490
|
+
const path3 = join97(input.projectRoot, lockRelative);
|
|
89491
|
+
const guard = join97(path3, ".recovery");
|
|
89405
89492
|
await mkdir33(guard);
|
|
89406
89493
|
let archived = false;
|
|
89407
89494
|
try {
|
|
89408
89495
|
const current2 = await inspectWriterLock(input.projectRoot);
|
|
89409
89496
|
if (current2?.digest !== before.digest || current2.process_state !== "not-running")
|
|
89410
89497
|
throw new Error("Writer lock changed or owner resumed; keep the lock.");
|
|
89411
|
-
const names = await
|
|
89498
|
+
const names = await readdir24(path3);
|
|
89412
89499
|
if (names.some((name3) => name3 !== "owner.json" && name3 !== ".recovery"))
|
|
89413
89500
|
throw new Error("Unexpected lock contents; preserve for diagnosis.");
|
|
89414
89501
|
const archive = `.tmp/context-runtime/locks/recovered-write-${randomUUID7()}.lock`;
|
|
89415
|
-
await rename8(path3,
|
|
89502
|
+
await rename8(path3, join97(input.projectRoot, archive));
|
|
89416
89503
|
archived = true;
|
|
89417
89504
|
return { action: "writer-lock-recovered", archived_lock: archive, next: "context task recover --format json" };
|
|
89418
89505
|
} finally {
|
|
@@ -89436,12 +89523,12 @@ __export(exports_taskRecovery, {
|
|
|
89436
89523
|
RECOVERY_COMMAND: () => RECOVERY_COMMAND
|
|
89437
89524
|
});
|
|
89438
89525
|
import { existsSync as existsSync26 } from "node:fs";
|
|
89439
|
-
import { dirname as
|
|
89440
|
-
import { lstat as lstat11, readdir as
|
|
89526
|
+
import { dirname as dirname40, join as join98 } from "node:path";
|
|
89527
|
+
import { lstat as lstat11, readdir as readdir25, readFile as readFile80 } from "node:fs/promises";
|
|
89441
89528
|
async function recoveryText(root2, path3) {
|
|
89442
89529
|
const target = await safeProjectTarget(root2, path3);
|
|
89443
89530
|
try {
|
|
89444
|
-
return await
|
|
89531
|
+
return await readFile80(target, "utf8");
|
|
89445
89532
|
} catch (error) {
|
|
89446
89533
|
if (error.code === "ENOENT")
|
|
89447
89534
|
return;
|
|
@@ -89454,7 +89541,7 @@ async function recoveryJournals(root2) {
|
|
|
89454
89541
|
await safeProjectTarget(root2, path3);
|
|
89455
89542
|
let stat10;
|
|
89456
89543
|
try {
|
|
89457
|
-
stat10 = await lstat11(
|
|
89544
|
+
stat10 = await lstat11(join98(root2, path3));
|
|
89458
89545
|
} catch (error) {
|
|
89459
89546
|
if (error.code === "ENOENT")
|
|
89460
89547
|
return;
|
|
@@ -89465,7 +89552,7 @@ async function recoveryJournals(root2) {
|
|
|
89465
89552
|
if (stat10.isDirectory()) {
|
|
89466
89553
|
if (depth > 3)
|
|
89467
89554
|
throw new TypeError("Unexpected transaction directory depth; preserve it for diagnosis.");
|
|
89468
|
-
for (const name3 of (await
|
|
89555
|
+
for (const name3 of (await readdir25(join98(root2, path3))).sort())
|
|
89469
89556
|
await visit4(`${path3}/${name3}`, depth + 1);
|
|
89470
89557
|
} else if (stat10.isFile())
|
|
89471
89558
|
entries2.push({ path: path3, digest: indexerProtocolDigest(await recoveryText(root2, path3)) });
|
|
@@ -89475,10 +89562,10 @@ async function recoveryJournals(root2) {
|
|
|
89475
89562
|
}
|
|
89476
89563
|
function recoveryResources() {
|
|
89477
89564
|
try {
|
|
89478
|
-
const root2 =
|
|
89565
|
+
const root2 = dirname40(contextWorkflowProviderPath());
|
|
89479
89566
|
const resources = {
|
|
89480
|
-
skill:
|
|
89481
|
-
issue_template:
|
|
89567
|
+
skill: join98(root2, "skills/recover-workspace/SKILL.md"),
|
|
89568
|
+
issue_template: join98(root2, "resources/templates/recovery-issue.md")
|
|
89482
89569
|
};
|
|
89483
89570
|
if (!Object.values(resources).every((path3) => existsSync26(path3)))
|
|
89484
89571
|
throw new Error("Recovery resources are absent from this Provider.");
|
|
@@ -89563,8 +89650,8 @@ var exports_taskLocalSourceAdjustment = {};
|
|
|
89563
89650
|
__export(exports_taskLocalSourceAdjustment, {
|
|
89564
89651
|
adjustLocalRevisionSources: () => adjustLocalRevisionSources
|
|
89565
89652
|
});
|
|
89566
|
-
import { readFile as
|
|
89567
|
-
import { join as
|
|
89653
|
+
import { readFile as readFile81 } from "node:fs/promises";
|
|
89654
|
+
import { join as join99 } from "node:path";
|
|
89568
89655
|
async function adjustLocalRevisionSources(root2, input) {
|
|
89569
89656
|
const { readMaintenance: readMaintenance2 } = await Promise.resolve().then(() => (init_maintenanceStorage(), exports_maintenanceStorage));
|
|
89570
89657
|
if ((await readMaintenance2(root2)).active && await readProductionStage(root2))
|
|
@@ -89588,7 +89675,7 @@ async function adjustLocalRevisionSources(root2, input) {
|
|
|
89588
89675
|
if (input.refresh && (!current2.refresh_sources || indexerProtocolDigest([...current2.refresh_sources].sort()) !== indexerProtocolDigest([...selected].sort()))) {
|
|
89589
89676
|
throw new TypeError("No matching acquisition adjustment exists. Run task adjust without refresh first.");
|
|
89590
89677
|
}
|
|
89591
|
-
const raw = await
|
|
89678
|
+
const raw = await readFile81(join99(root2, await revisionStoragePath(root2)), "utf8");
|
|
89592
89679
|
let next2;
|
|
89593
89680
|
const discardIds = new Set;
|
|
89594
89681
|
if (!input.refresh) {
|
|
@@ -89691,7 +89778,7 @@ ${input.instruction}` : revision.instruction
|
|
|
89691
89778
|
content: content3
|
|
89692
89779
|
}];
|
|
89693
89780
|
if (discardIds.size > 0) {
|
|
89694
|
-
const ledger = await
|
|
89781
|
+
const ledger = await readFile81(join99(root2, CANDIDATE_LEDGER_FILE), "utf8").catch((error) => {
|
|
89695
89782
|
if (error.code === "ENOENT")
|
|
89696
89783
|
return;
|
|
89697
89784
|
throw error;
|
|
@@ -89813,7 +89900,7 @@ var init_taskSourceAdjustment = __esm(() => {
|
|
|
89813
89900
|
});
|
|
89814
89901
|
|
|
89815
89902
|
// src/project/managedDocumentImport.ts
|
|
89816
|
-
import { readFile as
|
|
89903
|
+
import { readFile as readFile87 } from "node:fs/promises";
|
|
89817
89904
|
async function importManagedDocument(projectRoot, value) {
|
|
89818
89905
|
const input = inputSchema.parse(value);
|
|
89819
89906
|
if (input.type !== "sessions" && input.changes !== undefined)
|
|
@@ -89822,7 +89909,7 @@ async function importManagedDocument(projectRoot, value) {
|
|
|
89822
89909
|
const path3 = await assertManagedDocumentPath(projectRoot, input.type, input.name);
|
|
89823
89910
|
let previous3;
|
|
89824
89911
|
try {
|
|
89825
|
-
previous3 = await
|
|
89912
|
+
previous3 = await readFile87(path3, "utf8");
|
|
89826
89913
|
} catch (error) {
|
|
89827
89914
|
if (!(error && typeof error === "object" && ("code" in error) && error.code === "ENOENT"))
|
|
89828
89915
|
throw error;
|
|
@@ -89869,14 +89956,14 @@ var init_managedDocumentImport = __esm(() => {
|
|
|
89869
89956
|
});
|
|
89870
89957
|
|
|
89871
89958
|
// src/project/larkDocumentImport.ts
|
|
89872
|
-
import { readFile as
|
|
89959
|
+
import { readFile as readFile88 } from "node:fs/promises";
|
|
89873
89960
|
async function importLarkDocument(projectRoot, value) {
|
|
89874
89961
|
const input = schema3.parse(value);
|
|
89875
89962
|
const responsePages = [];
|
|
89876
89963
|
for (const path3 of input.response_files) {
|
|
89877
89964
|
assertActionInputWorkspace(projectRoot, path3);
|
|
89878
89965
|
const { resolve: resolve8 } = await import("node:path");
|
|
89879
|
-
responsePages.push(await
|
|
89966
|
+
responsePages.push(await readFile88(resolve8(projectRoot, path3), "utf8"));
|
|
89880
89967
|
}
|
|
89881
89968
|
const mediaFiles = {};
|
|
89882
89969
|
for (const [token, path3] of Object.entries(input.media_files ?? {})) {
|
|
@@ -89944,11 +90031,11 @@ var exports_managedDocumentRename = {};
|
|
|
89944
90031
|
__export(exports_managedDocumentRename, {
|
|
89945
90032
|
renameManagedDocument: () => renameManagedDocument
|
|
89946
90033
|
});
|
|
89947
|
-
import { readFile as
|
|
89948
|
-
import { join as
|
|
90034
|
+
import { readFile as readFile89 } from "node:fs/promises";
|
|
90035
|
+
import { join as join107, posix as posix11 } from "node:path";
|
|
89949
90036
|
async function optionalText2(path3) {
|
|
89950
90037
|
try {
|
|
89951
|
-
return await
|
|
90038
|
+
return await readFile89(path3, "utf8");
|
|
89952
90039
|
} catch (error) {
|
|
89953
90040
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
|
|
89954
90041
|
return;
|
|
@@ -90032,12 +90119,12 @@ async function renameManagedDocument(input) {
|
|
|
90032
90119
|
if (path3.split("/").includes("..") || path3.startsWith("/"))
|
|
90033
90120
|
throw new TypeError("Source references contain an unsafe knowledge path; repair it before renaming.");
|
|
90034
90121
|
await safeProjectTarget(input.projectRoot, path3);
|
|
90035
|
-
const before = await optionalText2(
|
|
90122
|
+
const before = await optionalText2(join107(input.projectRoot, path3));
|
|
90036
90123
|
if (before === undefined)
|
|
90037
90124
|
continue;
|
|
90038
90125
|
let after;
|
|
90039
90126
|
if (path3.endsWith(".yaml")) {
|
|
90040
|
-
const parsed =
|
|
90127
|
+
const parsed = import_yaml49.default.parse(before);
|
|
90041
90128
|
const updated = replace2(parsed);
|
|
90042
90129
|
if (path3 === "knowledge/structure.yaml") {
|
|
90043
90130
|
const rewritten = articles.map((article) => ({ ...article, sections: article.sections.map((section) => ({
|
|
@@ -90052,7 +90139,7 @@ async function renameManagedDocument(input) {
|
|
|
90052
90139
|
}
|
|
90053
90140
|
if (JSON.stringify(parsed) === JSON.stringify(updated))
|
|
90054
90141
|
continue;
|
|
90055
|
-
after =
|
|
90142
|
+
after = import_yaml49.default.stringify(updated);
|
|
90056
90143
|
} else
|
|
90057
90144
|
after = before.replace(reference2, nextRef);
|
|
90058
90145
|
if (path3 === "src/index.ts")
|
|
@@ -90104,7 +90191,7 @@ async function renameManagedDocument(input) {
|
|
|
90104
90191
|
};
|
|
90105
90192
|
});
|
|
90106
90193
|
}
|
|
90107
|
-
var
|
|
90194
|
+
var import_yaml49;
|
|
90108
90195
|
var init_managedDocumentRename = __esm(() => {
|
|
90109
90196
|
init_markdownLinks();
|
|
90110
90197
|
init_src2();
|
|
@@ -90116,12 +90203,12 @@ var init_managedDocumentRename = __esm(() => {
|
|
|
90116
90203
|
init_durableSingleFileTransaction();
|
|
90117
90204
|
init_durableMultiFileTransaction();
|
|
90118
90205
|
init_writeLock();
|
|
90119
|
-
|
|
90206
|
+
import_yaml49 = __toESM(require_dist(), 1);
|
|
90120
90207
|
});
|
|
90121
90208
|
|
|
90122
90209
|
// src/cli.ts
|
|
90123
90210
|
import { existsSync as existsSync36, realpathSync as realpathSync3 } from "node:fs";
|
|
90124
|
-
import { dirname as
|
|
90211
|
+
import { dirname as dirname47, join as join111 } from "node:path";
|
|
90125
90212
|
import { fileURLToPath as fileURLToPath10, pathToFileURL as pathToFileURL3 } from "node:url";
|
|
90126
90213
|
|
|
90127
90214
|
// ../../node_modules/.bun/commander@11.1.0/node_modules/commander/esm.mjs
|
|
@@ -90176,7 +90263,7 @@ init_dist();
|
|
|
90176
90263
|
init_cliFeedback();
|
|
90177
90264
|
init_errors3();
|
|
90178
90265
|
init_exitCode();
|
|
90179
|
-
import { join as
|
|
90266
|
+
import { join as join89 } from "node:path";
|
|
90180
90267
|
|
|
90181
90268
|
// src/project/status.ts
|
|
90182
90269
|
init_productionPlanning();
|
|
@@ -101011,200 +101098,21 @@ init_workflowFacts();
|
|
|
101011
101098
|
init_workflowProvider();
|
|
101012
101099
|
init_workflowTypes();
|
|
101013
101100
|
|
|
101014
|
-
// src/project/
|
|
101015
|
-
|
|
101016
|
-
|
|
101017
|
-
|
|
101018
|
-
"Omit all": "全部不收录",
|
|
101019
|
-
"Copy review results": "复制审核结果",
|
|
101020
|
-
"Toggle theme": "切换明暗主题",
|
|
101021
|
-
"Pages to review": "待审页面",
|
|
101022
|
-
"candidate filters": "按审核状态筛选",
|
|
101023
|
-
approved: "已批准",
|
|
101024
|
-
omitted: "不收录",
|
|
101025
|
-
pending: "待审核",
|
|
101026
|
-
"Search pages or modules": "搜索页面或模块",
|
|
101027
|
-
"Page content": "页面内容",
|
|
101028
|
-
Previous: "上一页",
|
|
101029
|
-
Next: "下一页",
|
|
101030
|
-
"Next pending": "下一个待审",
|
|
101031
|
-
"Review results": "审核结果",
|
|
101032
|
-
"These choices take effect only after you send the review code back to the conversation. Each segment is at most 980 characters. Send every segment before applying.": "将审核码发回会话后,这些选择才会生效。每段不超过 980 个字符;如果有多段,请全部发送后再应用。",
|
|
101033
|
-
"Previous segment": "上一段",
|
|
101034
|
-
"Next segment": "下一段",
|
|
101035
|
-
"review code": "审核码",
|
|
101036
|
-
Close: "关闭",
|
|
101037
|
-
Copy: "复制",
|
|
101038
|
-
"{count} pages · {scope} · {pending} pending · {approved} approved · {rejected} omitted": "{count} 页 · {scope} · {pending} 待审核 · {approved} 已批准 · {rejected} 不收录",
|
|
101039
|
-
"All collections": "全部分类",
|
|
101040
|
-
"Set all {count} pages in {group} to {status}?": "将 {group} 的全部 {count} 页设为“{status}”?",
|
|
101041
|
-
"Set all {count} pages to {status}?": "将全部 {count} 页设为“{status}”?",
|
|
101042
|
-
"Choices changed. Copy the updated code before applying.": "选择已变更,请复制更新后的审核码再应用。",
|
|
101043
|
-
"Select at least one page decision; pending pages remain for later review.": "请至少选择一页的审核结果;待审页面留待后续处理。",
|
|
101044
|
-
"Segment {part}/{total} · {length}/980 characters": "第 {part}/{total} 段 · {length}/980 字符",
|
|
101045
|
-
"No review code yet": "暂未生成审核码",
|
|
101046
|
-
"{approved} approved · {rejected} not included · {pending} pending": "{approved} 页批准 · {rejected} 页不收录 · {pending} 页待审核",
|
|
101047
|
-
"Pages not included": "不收录的页面",
|
|
101048
|
-
"Open review results": "查看审核结果",
|
|
101049
|
-
"{count} pending pages remain": "还有 {count} 页待审核",
|
|
101050
|
-
Copied: "已复制",
|
|
101051
|
-
"Copy manually from the textarea": "请从文本框中手动复制",
|
|
101052
|
-
"No draft candidates.": "当前没有待审页面。",
|
|
101053
|
-
"Nothing to review.": "没有需要审核的内容。",
|
|
101054
|
-
"No candidates match the current filters.": "没有符合当前筛选条件的页面。",
|
|
101055
|
-
"Adjust the candidate filters to continue reviewing.": "请调整筛选条件,继续审核。",
|
|
101056
|
-
"{count} items": "{count} 页",
|
|
101057
|
-
"evidence unavailable": "来源不可用",
|
|
101058
|
-
"Source snapshot unavailable. Restore it before approving this candidate, or omit the page.": "来源快照不可用。请先恢复来源再批准,或选择不收录此页。",
|
|
101059
|
-
"Source Markdown": "Markdown 原文",
|
|
101060
|
-
"Source locations": "来源位置",
|
|
101061
|
-
Approve: "批准",
|
|
101062
|
-
Omit: "不收录",
|
|
101063
|
-
"Need changes? Leave this page pending and ask the agent to repair it. Other reviewed pages can be approved.": "需要修改?本页保留待审,并让 Agent 返修。已审核的其他页面可以先批准。"
|
|
101064
|
-
};
|
|
101065
|
-
|
|
101066
|
-
// src/project/reviewCode.ts
|
|
101067
|
-
function createReviewCodeCodec() {
|
|
101068
|
-
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
101069
|
-
function checksum(text7) {
|
|
101070
|
-
let crc = 4294967295;
|
|
101071
|
-
for (let i2 = 0;i2 < text7.length; i2++) {
|
|
101072
|
-
crc ^= text7.charCodeAt(i2);
|
|
101073
|
-
for (let bit = 0;bit < 8; bit++)
|
|
101074
|
-
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
101075
|
-
}
|
|
101076
|
-
return ((crc ^ 4294967295) >>> 0).toString(16).padStart(8, "0");
|
|
101077
|
-
}
|
|
101078
|
-
function pack(bytes) {
|
|
101079
|
-
let value = 0, bits = 0, result = "";
|
|
101080
|
-
for (const byte of bytes) {
|
|
101081
|
-
value = value << 8 | byte;
|
|
101082
|
-
bits += 8;
|
|
101083
|
-
while (bits >= 6) {
|
|
101084
|
-
bits -= 6;
|
|
101085
|
-
result += alphabet[value >>> bits & 63];
|
|
101086
|
-
}
|
|
101087
|
-
}
|
|
101088
|
-
if (bits)
|
|
101089
|
-
result += alphabet[value << 6 - bits & 63];
|
|
101090
|
-
return result;
|
|
101091
|
-
}
|
|
101092
|
-
function unpack(text7) {
|
|
101093
|
-
if (!/^[A-Za-z0-9_-]*$/.test(text7))
|
|
101094
|
-
throw new Error("Invalid review code encoding");
|
|
101095
|
-
let value = 0, bits = 0;
|
|
101096
|
-
const bytes = [];
|
|
101097
|
-
for (const char of text7) {
|
|
101098
|
-
value = value << 6 | alphabet.indexOf(char);
|
|
101099
|
-
bits += 6;
|
|
101100
|
-
if (bits >= 8) {
|
|
101101
|
-
bits -= 8;
|
|
101102
|
-
bytes.push(value >>> bits & 255);
|
|
101103
|
-
}
|
|
101104
|
-
}
|
|
101105
|
-
if (pack(bytes) !== text7)
|
|
101106
|
-
throw new Error("Noncanonical review code encoding");
|
|
101107
|
-
return bytes;
|
|
101108
|
-
}
|
|
101109
|
-
function hash3(text7) {
|
|
101110
|
-
if (!/^[a-f0-9]{64}$/.test(text7))
|
|
101111
|
-
throw new Error("Review requires a complete candidate digest");
|
|
101112
|
-
return pack(Array.from({ length: 32 }, (_, i2) => Number.parseInt(text7.slice(i2 * 2, i2 * 2 + 2), 16)));
|
|
101113
|
-
}
|
|
101114
|
-
function unhash(text7) {
|
|
101115
|
-
const bytes = unpack(text7);
|
|
101116
|
-
if (bytes.length !== 32)
|
|
101117
|
-
throw new Error("Invalid candidate digest");
|
|
101118
|
-
return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
101119
|
-
}
|
|
101120
|
-
function encode(scope2, idsHash, contentHash2, statuses) {
|
|
101121
|
-
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !statuses.length || statuses.length > 1e6 || statuses.some((status) => status !== "approved" && status !== "rejected" && status !== "pending")) {
|
|
101122
|
-
throw new Error("Select at least one review decision and keep undecided pages pending");
|
|
101123
|
-
}
|
|
101124
|
-
if (statuses.every((s) => s === "pending"))
|
|
101125
|
-
throw new Error("Select at least one review decision");
|
|
101126
|
-
const mode = statuses.every((s) => s === "approved") ? "a" : statuses.every((s) => s === "rejected") ? "r" : statuses.includes("pending") ? "p" : "b";
|
|
101127
|
-
const bytes = Array(Math.ceil(statuses.length / (mode === "p" ? 4 : 8))).fill(0);
|
|
101128
|
-
if (mode === "p")
|
|
101129
|
-
statuses.forEach((s, i2) => {
|
|
101130
|
-
bytes[i2 >> 2] |= (s === "approved" ? 1 : s === "rejected" ? 2 : 0) << i2 % 4 * 2;
|
|
101131
|
-
});
|
|
101132
|
-
if (mode === "b")
|
|
101133
|
-
statuses.forEach((s, i2) => {
|
|
101134
|
-
if (s === "rejected")
|
|
101135
|
-
bytes[i2 >> 3] |= 1 << i2 % 8;
|
|
101136
|
-
});
|
|
101137
|
-
const body = ["CR1", scope2, statuses.length, hash3(idsHash), hash3(contentHash2), mode, mode === "b" || mode === "p" ? pack(bytes) : ""].join(".");
|
|
101138
|
-
const code = `${body}.${checksum(body)}`;
|
|
101139
|
-
if (code.length <= 980)
|
|
101140
|
-
return [code];
|
|
101141
|
-
const total = Math.ceil(code.length / 900);
|
|
101142
|
-
if (total > 200)
|
|
101143
|
-
throw new Error("Review decisions exceed 200 segments; use a smaller collection scope");
|
|
101144
|
-
const identity = checksum(code);
|
|
101145
|
-
return Array.from({ length: total }, (_, i2) => `CRP1.${identity}.${i2 + 1}.${total}.${code.slice(i2 * 900, (i2 + 1) * 900)}`);
|
|
101146
|
-
}
|
|
101147
|
-
function decode2(input) {
|
|
101148
|
-
if (input.length > 250000)
|
|
101149
|
-
throw new Error("Review code exceeds the supported size");
|
|
101150
|
-
const lines = input.trim().split(/\s+/);
|
|
101151
|
-
let code = lines[0];
|
|
101152
|
-
if (code.startsWith("CRP1.")) {
|
|
101153
|
-
const parts = new Map;
|
|
101154
|
-
let identity = "", total = 0;
|
|
101155
|
-
for (const line of lines) {
|
|
101156
|
-
const match = /^CRP1\.([a-f0-9]{8})\.([1-9][0-9]*)\.([1-9][0-9]*)\.(.+)$/.exec(line);
|
|
101157
|
-
if (!match || line.length > 980)
|
|
101158
|
-
throw new Error("Invalid review code segment");
|
|
101159
|
-
const index2 = Number(match[2]), count2 = Number(match[3]);
|
|
101160
|
-
if (count2 > 200 || index2 > count2 || parts.has(index2) || total && (total !== count2 || identity !== match[1])) {
|
|
101161
|
-
throw new Error("Duplicate or mixed review code segments");
|
|
101162
|
-
}
|
|
101163
|
-
identity = match[1];
|
|
101164
|
-
total = count2;
|
|
101165
|
-
parts.set(index2, match[4]);
|
|
101166
|
-
}
|
|
101167
|
-
if (parts.size !== total)
|
|
101168
|
-
throw new Error(`Missing review code segments: received ${parts.size} of ${total}; collect all segments before applying`);
|
|
101169
|
-
code = Array.from({ length: total }, (_, i2) => parts.get(i2 + 1)).join("");
|
|
101170
|
-
if (checksum(code) !== identity)
|
|
101171
|
-
throw new Error("Review code segment checksum mismatch");
|
|
101172
|
-
} else if (lines.length !== 1 || code.length > 980)
|
|
101173
|
-
throw new Error("Copy each complete review code segment unchanged");
|
|
101174
|
-
const fields = code.split(".");
|
|
101175
|
-
if (fields.length !== 8 || fields[0] !== "CR1" || checksum(fields.slice(0, 7).join(".")) !== fields[7]) {
|
|
101176
|
-
throw new Error("Review code is damaged or unsupported; copy it again from the report");
|
|
101177
|
-
}
|
|
101178
|
-
const [, scope2, countText, ids, content3, mode, data2] = fields;
|
|
101179
|
-
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !/^[1-9][0-9]*$/.test(countText))
|
|
101180
|
-
throw new Error("Invalid review scope");
|
|
101181
|
-
const count = Number(countText);
|
|
101182
|
-
if (count > 1e6 || !["a", "r", "b", "p"].includes(mode))
|
|
101183
|
-
throw new Error("Invalid review decisions");
|
|
101184
|
-
const bytes = unpack(data2);
|
|
101185
|
-
const perByte = mode === "p" ? 4 : 8;
|
|
101186
|
-
if (mode === "b" || mode === "p" ? bytes.length !== Math.ceil(count / perByte) || count % perByte !== 0 && bytes.at(-1) >>> count % perByte * (mode === "p" ? 2 : 1) !== 0 : data2 !== "") {
|
|
101187
|
-
throw new Error("Invalid review decision bitmap");
|
|
101188
|
-
}
|
|
101189
|
-
const statuses = Array.from({ length: count }, (_, i2) => {
|
|
101190
|
-
if (mode === "p") {
|
|
101191
|
-
const value = bytes[i2 >> 2] >>> i2 % 4 * 2 & 3;
|
|
101192
|
-
if (value === 3)
|
|
101193
|
-
throw new Error("Invalid pending review bitmap");
|
|
101194
|
-
return value === 1 ? "approved" : value === 2 ? "rejected" : "pending";
|
|
101195
|
-
}
|
|
101196
|
-
return mode === "r" || mode === "b" && bytes[i2 >> 3] & 1 << i2 % 8 ? "rejected" : "approved";
|
|
101197
|
-
});
|
|
101198
|
-
if (statuses.every((s) => s === "pending"))
|
|
101199
|
-
throw new Error("Review contains no decisions");
|
|
101200
|
-
return { scope: scope2, count, idsHash: unhash(ids), contentHash: unhash(content3), statuses };
|
|
101201
|
-
}
|
|
101202
|
-
return { encode, decode: decode2 };
|
|
101203
|
-
}
|
|
101101
|
+
// src/project/reviewHtml.ts
|
|
101102
|
+
init_candidateLedger();
|
|
101103
|
+
import { mkdir as mkdir30, writeFile as writeFile24 } from "node:fs/promises";
|
|
101104
|
+
import { dirname as dirname37, isAbsolute as isAbsolute15, join as join87, resolve as resolve28 } from "node:path";
|
|
101204
101105
|
|
|
101205
|
-
// src/project/
|
|
101106
|
+
// src/project/reviewSiteModel.ts
|
|
101107
|
+
init_src2();
|
|
101206
101108
|
init_unified();
|
|
101207
101109
|
init_remark_parse();
|
|
101110
|
+
var import_yaml40 = __toESM(require_dist(), 1);
|
|
101111
|
+
import { readFile as readFile68 } from "node:fs/promises";
|
|
101112
|
+
import { join as join86 } from "node:path";
|
|
101113
|
+
import { execFile as execFile10 } from "node:child_process";
|
|
101114
|
+
import { promisify as promisify10 } from "node:util";
|
|
101115
|
+
import { createHash as createHash24 } from "node:crypto";
|
|
101208
101116
|
|
|
101209
101117
|
// ../../node_modules/.bun/mdast-util-gfm-autolink-literal@2.0.1/node_modules/mdast-util-gfm-autolink-literal/lib/index.js
|
|
101210
101118
|
init_development();
|
|
@@ -104162,7 +104070,45 @@ function remarkGfm(options) {
|
|
|
104162
104070
|
fromMarkdownExtensions.push(gfmFromMarkdown());
|
|
104163
104071
|
toMarkdownExtensions.push(gfmToMarkdown(settings));
|
|
104164
104072
|
}
|
|
104073
|
+
// src/project/reviewSiteModel.ts
|
|
104074
|
+
init_workspace();
|
|
104075
|
+
|
|
104076
|
+
// src/project/reviewFeedback.ts
|
|
104077
|
+
import { readdir as readdir20, readFile as readFile67 } from "node:fs/promises";
|
|
104078
|
+
import { join as join85 } from "node:path";
|
|
104079
|
+
async function readPendingReviewFeedback(root2, candidates) {
|
|
104080
|
+
const directory = join85(root2, ".tmp/context-runtime/review-feedback");
|
|
104081
|
+
let files;
|
|
104082
|
+
try {
|
|
104083
|
+
files = await readdir20(directory);
|
|
104084
|
+
} catch (e) {
|
|
104085
|
+
if (e.code === "ENOENT")
|
|
104086
|
+
return [];
|
|
104087
|
+
throw e;
|
|
104088
|
+
}
|
|
104089
|
+
const pending = new Map(candidates.map((c) => [c.record.candidate_id, c.record.fingerprint]));
|
|
104090
|
+
const results = new Map;
|
|
104091
|
+
const receipts = [];
|
|
104092
|
+
for (const file of files.filter((f) => /^[a-f0-9]+\.json$/u.test(f)).sort()) {
|
|
104093
|
+
const receipt2 = JSON.parse(await readFile67(join85(directory, file), "utf8"));
|
|
104094
|
+
receipts.push(receipt2);
|
|
104095
|
+
}
|
|
104096
|
+
for (const receipt2 of receipts.sort((a, b) => a.created_at.localeCompare(b.created_at))) {
|
|
104097
|
+
for (const repair of receipt2.repairs)
|
|
104098
|
+
if (pending.get(repair.candidate_id) === repair.fingerprint)
|
|
104099
|
+
results.set(repair.candidate_id, { candidate_id: repair.candidate_id, path: repair.path, instruction: repair.instruction, command: repair.command });
|
|
104100
|
+
}
|
|
104101
|
+
return [...results.values()];
|
|
104102
|
+
}
|
|
104103
|
+
|
|
104104
|
+
// src/project/reviewSiteModel.ts
|
|
104105
|
+
init_knowledgeMap2();
|
|
104106
|
+
init_approvedKnowledgeMetadata();
|
|
104107
|
+
init_approvedFileRead();
|
|
104108
|
+
|
|
104165
104109
|
// src/project/reviewMarkdown.ts
|
|
104110
|
+
init_unified();
|
|
104111
|
+
init_remark_parse();
|
|
104166
104112
|
function escapeReviewHtml(value) {
|
|
104167
104113
|
return value.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """);
|
|
104168
104114
|
}
|
|
@@ -104244,172 +104190,286 @@ function renderReviewMarkdown(markdown, pageTitle) {
|
|
|
104244
104190
|
return (pageTitle && !hasPageHeading ? `<h1>${escapeReviewHtml(pageTitle)}</h1>` : "") + render(tree);
|
|
104245
104191
|
}
|
|
104246
104192
|
|
|
104247
|
-
// src/project/
|
|
104248
|
-
|
|
104249
|
-
|
|
104250
|
-
|
|
104251
|
-
|
|
104252
|
-
|
|
104253
|
-
|
|
104254
|
-
|
|
104255
|
-
|
|
104256
|
-
|
|
104257
|
-
|
|
104258
|
-
|
|
104259
|
-
|
|
104260
|
-
|
|
104261
|
-
|
|
104262
|
-
|
|
104263
|
-
|
|
104264
|
-
|
|
104265
|
-
|
|
104266
|
-
|
|
104267
|
-
|
|
104268
|
-
|
|
104269
|
-
|
|
104270
|
-
}
|
|
104271
|
-
|
|
104272
|
-
|
|
104273
|
-
|
|
104274
|
-
|
|
104275
|
-
|
|
104276
|
-
|
|
104277
|
-
|
|
104278
|
-
|
|
104279
|
-
|
|
104280
|
-
|
|
104281
|
-
|
|
104282
|
-
[
|
|
104283
|
-
|
|
104284
|
-
|
|
104285
|
-
|
|
104286
|
-
|
|
104287
|
-
|
|
104288
|
-
|
|
104289
|
-
|
|
104290
|
-
|
|
104291
|
-
|
|
104292
|
-
|
|
104293
|
-
|
|
104294
|
-
|
|
104295
|
-
|
|
104296
|
-
|
|
104297
|
-
|
|
104298
|
-
|
|
104299
|
-
|
|
104300
|
-
|
|
104301
|
-
|
|
104302
|
-
|
|
104303
|
-
.
|
|
104304
|
-
|
|
104305
|
-
|
|
104306
|
-
|
|
104307
|
-
|
|
104308
|
-
|
|
104309
|
-
|
|
104310
|
-
|
|
104311
|
-
|
|
104312
|
-
|
|
104313
|
-
|
|
104314
|
-
|
|
104315
|
-
|
|
104316
|
-
|
|
104317
|
-
|
|
104318
|
-
|
|
104319
|
-
|
|
104320
|
-
|
|
104321
|
-
|
|
104322
|
-
|
|
104323
|
-
|
|
104324
|
-
|
|
104325
|
-
|
|
104326
|
-
|
|
104327
|
-
|
|
104328
|
-
|
|
104329
|
-
|
|
104330
|
-
|
|
104331
|
-
|
|
104332
|
-
|
|
104333
|
-
|
|
104334
|
-
|
|
104335
|
-
|
|
104336
|
-
|
|
104337
|
-
|
|
104338
|
-
|
|
104339
|
-
|
|
104340
|
-
|
|
104341
|
-
|
|
104342
|
-
|
|
104343
|
-
.
|
|
104344
|
-
.
|
|
104345
|
-
|
|
104346
|
-
|
|
104347
|
-
|
|
104348
|
-
|
|
104349
|
-
|
|
104350
|
-
|
|
104351
|
-
|
|
104352
|
-
|
|
104353
|
-
|
|
104354
|
-
|
|
104355
|
-
|
|
104356
|
-
|
|
104357
|
-
|
|
104358
|
-
|
|
104359
|
-
|
|
104360
|
-
|
|
104361
|
-
|
|
104362
|
-
|
|
104363
|
-
|
|
104364
|
-
|
|
104365
|
-
|
|
104366
|
-
|
|
104367
|
-
|
|
104368
|
-
|
|
104369
|
-
|
|
104370
|
-
|
|
104371
|
-
|
|
104372
|
-
|
|
104373
|
-
|
|
104374
|
-
.
|
|
104375
|
-
|
|
104376
|
-
|
|
104377
|
-
|
|
104378
|
-
|
|
104379
|
-
|
|
104380
|
-
|
|
104381
|
-
.
|
|
104382
|
-
.
|
|
104383
|
-
.
|
|
104384
|
-
.
|
|
104385
|
-
|
|
104386
|
-
|
|
104387
|
-
|
|
104388
|
-
|
|
104389
|
-
|
|
104390
|
-
|
|
104391
|
-
|
|
104392
|
-
|
|
104393
|
-
|
|
104394
|
-
|
|
104395
|
-
|
|
104396
|
-
|
|
104397
|
-
|
|
104398
|
-
|
|
104399
|
-
.header { grid-template-columns:minmax(0, 1fr); }
|
|
104400
|
-
.toolbar { justify-content:flex-start; flex-wrap:wrap; }
|
|
104401
|
-
body { overflow:auto; }
|
|
104402
|
-
.shell { height:auto; min-height:100vh; overflow:visible; padding:18px; }
|
|
104403
|
-
.layout { grid-template-columns:1fr; }
|
|
104404
|
-
.candidate-panel { max-height:42vh; }
|
|
104405
|
-
.detail-panel { display:block; overflow:visible; }
|
|
104406
|
-
.detail { overflow:visible; }
|
|
104407
|
-
.detail-titlebar { display:grid; }
|
|
104193
|
+
// src/project/reviewSiteModel.ts
|
|
104194
|
+
var hash3 = (s) => createHash24("sha256").update(s).digest("hex");
|
|
104195
|
+
var body = (s) => s.replace(/^---\r?\n[\s\S]*?\r?\n---\s*/u, "").replace(/<!--[^]*?-->/gu, "").trim();
|
|
104196
|
+
var title = (s, fallback) => {
|
|
104197
|
+
const front = /^---\r?\n([\s\S]*?)\r?\n---/u.exec(s);
|
|
104198
|
+
const value = front ? import_yaml40.parse(front[1])?.title : undefined;
|
|
104199
|
+
return typeof value === "string" ? value : /^#\s+(.+)$/mu.exec(s)?.[1] ?? fallback;
|
|
104200
|
+
};
|
|
104201
|
+
async function optional2(path3) {
|
|
104202
|
+
try {
|
|
104203
|
+
return await readFile68(path3, "utf8");
|
|
104204
|
+
} catch (e) {
|
|
104205
|
+
if (e.code === "ENOENT")
|
|
104206
|
+
return;
|
|
104207
|
+
throw e;
|
|
104208
|
+
}
|
|
104209
|
+
}
|
|
104210
|
+
async function reviewSiteBaselineHash(root2, reviewedPaths) {
|
|
104211
|
+
const files = await readApprovedMarkdownFiles(root2);
|
|
104212
|
+
return hash3(JSON.stringify([
|
|
104213
|
+
await optional2(join86(root2, "src/knowledge-map.yaml")) ?? null,
|
|
104214
|
+
files.map((f) => [f.relPath, reviewedPaths.includes(f.relPath) ? hash3(f.content) : title(f.content, f.relPath)]).sort((a, b) => a[0].localeCompare(b[0]))
|
|
104215
|
+
]));
|
|
104216
|
+
}
|
|
104217
|
+
function reviewBodyDiff(previous3, next) {
|
|
104218
|
+
function blocks(markdown) {
|
|
104219
|
+
const text9 = body(markdown);
|
|
104220
|
+
const tree = unified().use(remarkParse).use(remarkGfm).parse(text9);
|
|
104221
|
+
const definitions = tree.children.filter((node3) => node3.type === "definition").map((node3) => text9.slice(node3.position?.start.offset, node3.position?.end.offset)).join(`
|
|
104222
|
+
`);
|
|
104223
|
+
return tree.children.filter((node3) => node3.type !== "definition").map((node3) => renderReviewMarkdown(text9.slice(node3.position?.start.offset, node3.position?.end.offset) + `
|
|
104224
|
+
|
|
104225
|
+
` + definitions));
|
|
104226
|
+
}
|
|
104227
|
+
const before = blocks(previous3), after = blocks(next);
|
|
104228
|
+
const remaining = [...before];
|
|
104229
|
+
let omitted = false;
|
|
104230
|
+
const result = after.map((block) => {
|
|
104231
|
+
const exact = remaining.indexOf(block);
|
|
104232
|
+
if (exact >= 0) {
|
|
104233
|
+
remaining.splice(exact, 1);
|
|
104234
|
+
if (/^<h[1-6]>/u.test(block)) {
|
|
104235
|
+
omitted = false;
|
|
104236
|
+
return block;
|
|
104237
|
+
}
|
|
104238
|
+
if (omitted)
|
|
104239
|
+
return "";
|
|
104240
|
+
omitted = true;
|
|
104241
|
+
return '<div class="unchanged" data-label="unchanged">Unchanged content omitted.</div>';
|
|
104242
|
+
}
|
|
104243
|
+
omitted = false;
|
|
104244
|
+
return `<section class="changed"><span class="badge modify">Modify</span>${block}</section>`;
|
|
104245
|
+
});
|
|
104246
|
+
if (remaining.length)
|
|
104247
|
+
result.push(`<section class="changed"><span class="badge modify">Modify</span><details open><summary data-label="removed">Previous or removed content</summary>${remaining.join(`
|
|
104248
|
+
`)}</details></section>`);
|
|
104249
|
+
return result.join(`
|
|
104250
|
+
`);
|
|
104251
|
+
}
|
|
104252
|
+
async function collectReviewSiteModel(root2, candidates) {
|
|
104253
|
+
const pendingFeedback = new Map((await readPendingReviewFeedback(root2, candidates)).map((r) => [r.candidate_id, r.instruction]));
|
|
104254
|
+
const current2 = await readKnowledgeMap(root2);
|
|
104255
|
+
const metadata = await readApprovedKnowledgeMetadataIndex(root2);
|
|
104256
|
+
const articles = metadata.structure?.articles ?? [];
|
|
104257
|
+
const files = await readApprovedMarkdownFiles(root2);
|
|
104258
|
+
const byPath = new Map(files.map((f) => [f.relPath, f.content]));
|
|
104259
|
+
const pages = files.map((f) => ({
|
|
104260
|
+
id: articles.find((a) => a.path === f.relPath)?.article_id ?? f.relPath,
|
|
104261
|
+
path: f.relPath,
|
|
104262
|
+
title: title(f.content, f.relPath),
|
|
104263
|
+
change: "unchanged",
|
|
104264
|
+
html: "",
|
|
104265
|
+
sources: []
|
|
104266
|
+
}));
|
|
104267
|
+
for (const { record: r } of candidates) {
|
|
104268
|
+
const found = pages.find((p) => p.id === r.article_id || p.path === (r.approved_revision?.previous_path ?? r.path));
|
|
104269
|
+
const old = found && byPath.get(found.path);
|
|
104270
|
+
const next = r.indexer_candidate.sections.map((s) => s.markdown).join(`
|
|
104271
|
+
|
|
104272
|
+
`);
|
|
104273
|
+
const page = {
|
|
104274
|
+
id: r.article_id,
|
|
104275
|
+
candidate_id: r.candidate_id,
|
|
104276
|
+
title: r.review.title,
|
|
104277
|
+
path: r.path,
|
|
104278
|
+
...pendingFeedback.has(r.candidate_id) ? { revisionInstruction: pendingFeedback.get(r.candidate_id) } : {},
|
|
104279
|
+
...found && found.path !== r.path ? { previousPath: found.path } : {},
|
|
104280
|
+
change: found ? "modify" : "new",
|
|
104281
|
+
html: old === undefined ? renderReviewMarkdown(next.replace(/^# [^\n]+\n*/u, "")) : reviewBodyDiff(body(old).replace(/^# [^\n]+\n*/u, ""), next.replace(/^# [^\n]+\n*/u, "")),
|
|
104282
|
+
sources: [...r.source_refs, ...r.indexer_candidate.sections.flatMap((s) => s.references.map((ref2) => JSON.stringify(ref2)))]
|
|
104283
|
+
};
|
|
104284
|
+
if (found)
|
|
104285
|
+
pages.splice(pages.indexOf(found), 1, page);
|
|
104286
|
+
else
|
|
104287
|
+
pages.push(page);
|
|
104288
|
+
}
|
|
104289
|
+
let baseline = current2, navigationBaseline = files.length ? "current" : "empty";
|
|
104290
|
+
if (files.length) {
|
|
104291
|
+
try {
|
|
104292
|
+
const { stdout } = await promisify10(execFile10)("git", ["show", "HEAD:./src/knowledge-map.yaml"], { cwd: root2, timeout: 5000, maxBuffer: 4 * 1024 * 1024 });
|
|
104293
|
+
baseline = validateKnowledgeMap(import_yaml40.parse(stdout));
|
|
104294
|
+
navigationBaseline = "git-head";
|
|
104295
|
+
} catch {}
|
|
104296
|
+
}
|
|
104297
|
+
const nodes = (current2?.entries ?? []).map((n) => {
|
|
104298
|
+
const old = baseline?.entries.find((b) => b.key === n.key);
|
|
104299
|
+
const page = n.target && pages.find((p) => p.id === n.target?.artifact_ref);
|
|
104300
|
+
return {
|
|
104301
|
+
key: n.key,
|
|
104302
|
+
parent: n.parent ?? null,
|
|
104303
|
+
title: n.title,
|
|
104304
|
+
order: n.order ?? 0,
|
|
104305
|
+
...page ? { page: page.id } : {},
|
|
104306
|
+
...old && old.title !== n.title ? { oldTitle: old.title } : {},
|
|
104307
|
+
change: navigationBaseline === "empty" || !old ? "new" : JSON.stringify(old) !== JSON.stringify(n) ? "modify" : "unchanged"
|
|
104308
|
+
};
|
|
104309
|
+
});
|
|
104310
|
+
for (const n of baseline?.entries ?? [])
|
|
104311
|
+
if (!nodes.some((v) => v.key === n.key))
|
|
104312
|
+
nodes.push({
|
|
104313
|
+
key: n.key,
|
|
104314
|
+
parent: n.parent ?? null,
|
|
104315
|
+
title: n.title,
|
|
104316
|
+
order: n.order ?? 0,
|
|
104317
|
+
removed: true,
|
|
104318
|
+
change: "modify"
|
|
104319
|
+
});
|
|
104320
|
+
const unplaced = pages.filter((p) => p.candidate_id && !nodes.some((n) => n.page === p.id));
|
|
104321
|
+
if (unplaced.length) {
|
|
104322
|
+
nodes.push({ key: "review-unplaced", parent: null, title: "Unplaced articles", order: Number.MAX_SAFE_INTEGER, change: "new" });
|
|
104323
|
+
for (const [i2, p] of unplaced.entries())
|
|
104324
|
+
nodes.push({ key: `review-page-${p.id}`, parent: "review-unplaced", title: p.title, order: i2, page: p.id, change: p.change });
|
|
104325
|
+
}
|
|
104326
|
+
const pkgText = await optional2(join86(root2, "package.json"));
|
|
104327
|
+
const pkg = pkgText ? JSON.parse(pkgText) : {};
|
|
104328
|
+
const project = await optional2(join86(root2, "src/index.ts")) === undefined ? undefined : await loadContextProjectModule(root2);
|
|
104329
|
+
const siteTitle = project?.project.packages.flatMap((p) => p.kind === "package.kb" && p.site?.title ? [p.site.title] : [])[0];
|
|
104330
|
+
return { title: siteTitle ?? pkg.name ?? "Knowledge review", baselineHash: await reviewSiteBaselineHash(root2, candidates.map((c) => c.record.approved_revision?.previous_path ?? c.record.path)), nodes, pages, navigationBaseline };
|
|
104331
|
+
}
|
|
104332
|
+
var reviewHtmlJson = (value) => JSON.stringify(value).replace(/</gu, "\\u003c").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029");
|
|
104333
|
+
|
|
104334
|
+
// src/project/reviewFeedbackCode.ts
|
|
104335
|
+
function createReviewFeedbackCodec() {
|
|
104336
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
104337
|
+
function checksum(text9) {
|
|
104338
|
+
let crc = 4294967295;
|
|
104339
|
+
for (let i2 = 0;i2 < text9.length; i2++) {
|
|
104340
|
+
crc ^= text9.charCodeAt(i2);
|
|
104341
|
+
for (let j = 0;j < 8; j++)
|
|
104342
|
+
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
104343
|
+
}
|
|
104344
|
+
return ((crc ^ 4294967295) >>> 0).toString(16).padStart(8, "0");
|
|
104408
104345
|
}
|
|
104346
|
+
function validate(value) {
|
|
104347
|
+
if (!value || !/^[a-z][a-z0-9-]*$/.test(value.scope) || ![value.idsHash, value.contentHash, value.baselineHash].every((h) => typeof h === "string" && /^[a-f0-9]{64}$/.test(h)) || !Array.isArray(value.statuses) || !value.statuses.length || value.statuses.length > 1e5 || value.statuses.some((s) => !["approved", "rejected", "pending", "revised"].includes(s)) || value.statuses.every((s) => s === "pending") || !Array.isArray(value.repairs))
|
|
104348
|
+
throw new Error("Invalid review feedback scope or decisions");
|
|
104349
|
+
const seen = new Set;
|
|
104350
|
+
for (const repair of value.repairs) {
|
|
104351
|
+
if (!repair || !Number.isInteger(repair.index) || seen.has(repair.index) || value.statuses[repair.index] !== "revised" || typeof repair.instruction !== "string" || !repair.instruction.trim() || repair.instruction.length > 20000)
|
|
104352
|
+
throw new Error("Invalid or conflicting revision instruction");
|
|
104353
|
+
seen.add(repair.index);
|
|
104354
|
+
}
|
|
104355
|
+
if (value.statuses.filter((s) => s === "revised").length !== seen.size)
|
|
104356
|
+
throw new Error("Missing revision instruction");
|
|
104357
|
+
return value;
|
|
104358
|
+
}
|
|
104359
|
+
function encode(value) {
|
|
104360
|
+
validate(value);
|
|
104361
|
+
const bytes = new TextEncoder().encode(JSON.stringify({ scope: value.scope, idsHash: value.idsHash, contentHash: value.contentHash, baselineHash: value.baselineHash, statuses: value.statuses }));
|
|
104362
|
+
let bits = 0, carry = 0, data2 = "";
|
|
104363
|
+
for (const byte of bytes) {
|
|
104364
|
+
carry = carry << 8 | byte;
|
|
104365
|
+
bits += 8;
|
|
104366
|
+
while (bits >= 6) {
|
|
104367
|
+
bits -= 6;
|
|
104368
|
+
data2 += alphabet[carry >>> bits & 63];
|
|
104369
|
+
}
|
|
104370
|
+
}
|
|
104371
|
+
if (bits)
|
|
104372
|
+
data2 += alphabet[carry << 6 - bits & 63];
|
|
104373
|
+
const body2 = `CR2.${data2}`;
|
|
104374
|
+
const repairs = value.repairs.map((r) => JSON.stringify([r.index, r.instruction])).join(`
|
|
104375
|
+
`);
|
|
104376
|
+
const trailer = repairs ? `
|
|
104377
|
+
${repairs}` : "";
|
|
104378
|
+
const code3 = `${body2}.${checksum(body2 + trailer)}${trailer}`;
|
|
104379
|
+
if (code3.length > 250000)
|
|
104380
|
+
throw new Error("Review feedback is too large; shorten instructions or review a smaller scope");
|
|
104381
|
+
return code3;
|
|
104382
|
+
}
|
|
104383
|
+
function decode2(raw) {
|
|
104384
|
+
const code3 = raw.trim();
|
|
104385
|
+
if (code3.length > 250000)
|
|
104386
|
+
throw new Error("Review feedback exceeds supported size");
|
|
104387
|
+
const [header, ...lines] = code3.split(`
|
|
104388
|
+
`);
|
|
104389
|
+
const trailer = lines.length ? `
|
|
104390
|
+
${lines.join(`
|
|
104391
|
+
`)}` : "";
|
|
104392
|
+
const match = /^CR2\.([A-Za-z0-9_-]+)\.([a-f0-9]{8})$/.exec(header);
|
|
104393
|
+
if (!match || checksum(`CR2.${match[1]}` + trailer) !== match[2])
|
|
104394
|
+
throw new Error("Review feedback is damaged or incomplete; copy the full code again");
|
|
104395
|
+
let bits = 0, carry = 0;
|
|
104396
|
+
const bytes = [];
|
|
104397
|
+
for (const char of match[1]) {
|
|
104398
|
+
carry = carry << 6 | alphabet.indexOf(char);
|
|
104399
|
+
bits += 6;
|
|
104400
|
+
if (bits >= 8) {
|
|
104401
|
+
bits -= 8;
|
|
104402
|
+
bytes.push(carry >>> bits & 255);
|
|
104403
|
+
}
|
|
104404
|
+
}
|
|
104405
|
+
const headerValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(bytes)));
|
|
104406
|
+
const value = validate({ ...headerValue, repairs: lines.map((line) => {
|
|
104407
|
+
const row = JSON.parse(line);
|
|
104408
|
+
if (!Array.isArray(row) || row.length !== 2)
|
|
104409
|
+
throw new Error("Invalid revision instruction line");
|
|
104410
|
+
return { index: row[0], instruction: row[1] };
|
|
104411
|
+
}) });
|
|
104412
|
+
if (encode(value) !== code3)
|
|
104413
|
+
throw new Error("Noncanonical review feedback encoding");
|
|
104414
|
+
return value;
|
|
104415
|
+
}
|
|
104416
|
+
return { encode, decode: decode2 };
|
|
104417
|
+
}
|
|
104418
|
+
|
|
104419
|
+
// src/project/reviewSiteClient.ts
|
|
104420
|
+
var REVIEW_SITE_CLIENT = String.raw`
|
|
104421
|
+
const $=id=>document.getElementById(id);
|
|
104422
|
+
let language=(navigator.languages?.[0]||navigator.language||'en').startsWith('zh')?'zh':'en';
|
|
104423
|
+
const words={newRoots:['新增一级目录','New top-level categories'],ackRoots:['我已知晓本次新增一级目录','I acknowledge the new top-level categories'],home:['待审核内容','Pages to review'],unchanged:['本次未变更内容略。','Unchanged content omitted.'],previous:['查看旧文本','Previous text'],removed:['删除的内容','Removed content'],unplaced:['待落位文章','Unplaced articles'],approve:['批准这篇','Approve page'],reject:['拒绝这篇','Reject page'],revise:['修订','Revise'],approved:['已批准','Approved'],rejected:['已拒绝','Rejected'],revised:['已修订','Revision requested'],cancel:['取消','Cancel'],copy:['复制审核码','Copy review code'],allApprove:['全部批准','Approve all'],allReject:['全部拒绝','Reject all'],note:['输入修订意见','Enter revision instructions'],guide:['逐篇阅读并批准或拒绝后,复制审核码回复给 Agent。需要修改的文章请填写修订意见。','Read each page, approve or reject, then copy the review code back to your Agent. Enter instructions for pages needing revision.'],known:['知道了','Got it'],close:['关闭','Close'],notReviewed:['尚未完成审核','Not yet reviewed'],confirmAll:['建议逐篇阅读并确认。除非已读完所有待审核文章,否则请勿一次性全部批准。已有拒绝和修订意见将保留。','Read and confirm each page. Approve all only after reading every pending page. Existing rejections and revisions are preserved.'],confirm:['已阅读,全部批准','Read all, approve'],copied:['审核码已复制','Review code copied'],failed:['复制失败,请手动复制下方完整内容','Copy failed. Copy the complete text below manually.'],instructions:['请回到和 Agent 的会话窗口粘贴已复制内容进行回复即可继续~','Return to your conversation with the Agent and reply with the copied content to continue.'],long:['超过 1000 字符,飞书表单可能不接受。飞书场景下建议 @Bot 后粘贴回复。','Over 1,000 characters: a Feishu form may reject it. Mention @Bot and paste it in a reply instead.'],files:['预期工作区变化','Expected workspace changes'],navigate:['目录与文章','Directories and articles'],pendingNew:['未审批的新增文章','Pending new pages'],pendingModify:['未审批的修改文章','Pending modified pages'],processed:['已经审核和修订的文章','Reviewed or revision requested'],noSelection:['请先选择审核结果或填写修订意见','Select a decision or enter revision instructions first'],baseline:['无 Git 导航基线,目录沿用当前工作区;未推断历史目录变化。','No Git navigation baseline. Current navigation is shown without inferred historic changes.'],placement:['待落位文章不是新的站点栏目;请先按既有分类完成导航规划。','Unplaced articles are not a new site category. Finish their placement in the existing navigation first.']};
|
|
104424
|
+
const t=k=>words[k]?.[language==='zh'?0:1]||k;
|
|
104425
|
+
const escape=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
104426
|
+
const decisions=new Map(),notes=new Map(),expanded=new Set();let selected=null,active=null;
|
|
104427
|
+
const candidates=DATA.pages.filter(p=>p.candidate_id),ordered=[...candidates].sort((a,b)=>a.candidate_id<b.candidate_id?-1:1);
|
|
104428
|
+
for(const p of candidates)if(p.revisionInstruction){decisions.set(p.candidate_id,'revised');notes.set(p.candidate_id,p.revisionInstruction)}
|
|
104429
|
+
const badge=change=>change==='unchanged'?'':'<span class="badge '+change+'">'+(change==='new'?'New':change==='modify'?'Modify':t(change))+'</span>';
|
|
104430
|
+
const state=p=>decisions.get(p.candidate_id)||'pending';
|
|
104431
|
+
function descendants(key,seen=new Set()){if(seen.has(key))return[];seen.add(key);return DATA.nodes.filter(n=>n.parent===key).flatMap(n=>[n,...descendants(n.key,seen)])}
|
|
104432
|
+
function nodeChange(n){const changes=[n.change,...[n,...descendants(n.key)].flatMap(v=>{const p=DATA.pages.find(p=>p.id===v.page);return p?[p.change]:[]}),...descendants(n.key).map(v=>v.change)];return changes.includes('modify')?'modify':changes.includes('new')?'new':'unchanged'}
|
|
104433
|
+
function nodeTitle(n){return n.key==='review-unplaced'?t('unplaced'):n.title}
|
|
104434
|
+
function nodeLabel(n){return (n.removed?'<del>':'')+escape(nodeTitle(n))+(n.removed?'</del>':'')+(n.oldTitle?'<small class="old-title"> ← '+escape(n.oldTitle)+'</small>':'')}
|
|
104435
|
+
function totals(){const result={new:0,modify:0,approved:0,rejected:0,revised:0,pending:0};for(const p of candidates){const s=state(p);result[s]++;if(s==='pending')result[p.change==='new'?'new':'modify']++}return result}
|
|
104436
|
+
function updateCounts(){const c=totals();$('counts').textContent=c.new+' New / '+c.modify+' Modify / '+(c.approved+c.rejected+c.revised)+' Confirm';$('counter-pop').innerHTML='<div class="counter-grid"><div><b>'+c.new+'</b>'+t('pendingNew')+'</div><div><b>'+c.modify+'</b>'+t('pendingModify')+'</div></div><p>'+t('processed')+': '+(c.approved+c.rejected+c.revised)+'</p><small>'+t('approved')+' '+c.approved+' · '+t('rejected')+' '+c.rejected+' · '+t('revised')+' '+c.revised+'</small>'}
|
|
104437
|
+
function button(n,depth){const change=nodeChange(n),page=DATA.pages.find(p=>p.id===n.page);return '<button data-node="'+escape(n.key)+'" class="node '+change+(selected===n.page?' selected':'')+'" style="padding-left:'+(16+14*depth)+'px">'+nodeLabel(n)+badge(change)+(page&&page.candidate_id&&state(page)!=='pending'?badge(state(page)):'')+(DATA.nodes.some(c=>c.parent===n.key)?'<span class="caret">›</span>':'')+'</button>'}
|
|
104438
|
+
function renderNav(){const roots=DATA.nodes.filter(n=>!n.parent).sort((a,b)=>a.order-b.order);$('top').innerHTML=roots.map(n=>'<button data-root="'+escape(n.key)+'" class="'+nodeChange(n)+(active===n.key?' active':'')+'">'+nodeLabel(n)+badge(nodeChange(n))+'</button>').join('');let html='';function walk(key,depth,seen=new Set()){if(seen.has(key))return;seen.add(key);for(const n of DATA.nodes.filter(n=>n.parent===key).sort((a,b)=>a.order-b.order)){html+=button(n,depth);if(expanded.has(n.key)||n.page===selected||descendants(n.key).some(d=>d.page===selected))walk(n.key,depth+1,seen)}}if(active)walk(active,0);else html=roots.map(n=>button(n,0)).join('');$('tree').innerHTML=html}
|
|
104439
|
+
function rootFor(page){let node=DATA.nodes.find(n=>n.page===page);const seen=new Set();while(node?.parent&&!seen.has(node.key)){seen.add(node.key);node=DATA.nodes.find(n=>n.key===node.parent)}return node?.key||null}
|
|
104440
|
+
function showPage(id){selected=id;active=rootFor(id)||active;render()}
|
|
104441
|
+
function localizeBody(){document.querySelectorAll('[data-label]').forEach(el=>{el.textContent=t(el.dataset.label)})}
|
|
104442
|
+
function workspaceTree(){const tree={};for(const p of candidates){let node=tree;for(const part of ('knowledge/'+p.path).split('/'))node=node[part]??=( {} );node.$page=p}function lines(node,level=0){return Object.entries(node).filter(([k])=>k!=='$page').sort(([a],[b])=>a.localeCompare(b)).map(([k,v])=>'<div style="padding-left:'+level*18+'px">'+(v.$page?'<button data-page="'+escape(v.$page.id)+'">'+escape(k)+'</button>'+badge(v.$page.change)+(state(v.$page)!=='pending'?badge(state(v.$page)):''):escape(k)+'/')+'</div>'+lines(v,level+1)).join('')}return '<div class="workspace-tree">'+lines(tree)+'</div>'}
|
|
104443
|
+
function renderHome(){const c=totals();$('article').innerHTML='<h1>'+t('home')+'</h1><p class="stats">'+candidates.length+' '+(language==='zh'?'篇候选正文':'candidate pages')+' · '+c.approved+' '+t('approved')+' '+badge('new')+' '+badge('modify')+'</p>'+(DATA.navigationBaseline==='current'?'<p class="note">'+t('baseline')+'</p>':'')+(DATA.nodes.some(n=>n.key==='review-unplaced')?'<p class="note">'+t('placement')+'</p>':'')+'<h2>'+t('navigate')+'</h2>'+DATA.nodes.filter(n=>!n.parent).map(n=>{const ids=new Set([n,...descendants(n.key)].map(x=>x.page));const pages=candidates.filter(p=>ids.has(p.id));return pages.length?'<section class="home-group"><h3>'+nodeLabel(n)+badge(nodeChange(n))+'</h3><div class="cards">'+pages.map(p=>'<button data-page="'+escape(p.id)+'">'+escape(p.title)+badge(p.change)+(state(p)!=='pending'?badge(state(p)):'')+'</button>').join('')+'</div></section>':''}).join('')+'<h2>'+t('files')+'</h2>'+workspaceTree()}
|
|
104444
|
+
function controls(){const p=DATA.pages.find(p=>p.id===selected),s=p?state(p):'pending';$('footer').hidden=!p?.candidate_id;if(!p?.candidate_id)return;$('revision-note').value=notes.get(p.candidate_id)||'';$('revision-note').placeholder=t('note');$('revision-note').disabled=s==='approved'||s==='rejected';for(const [id,v,label]of[['revise-btn','revised','revise'],['reject-btn','rejected','reject'],['approve-btn','approved','approve']]){const b=$(id);b.disabled=s!=='pending'&&s!==v;b.className='btn '+(s===v?'chosen':v==='approved'?'primary':'');b.innerHTML=s===v?'<span class="normal">'+t(v)+'</span><span class="hover-label">'+t('cancel')+'</span>':t(label);b.title=s===v?t('cancel'):''}}
|
|
104445
|
+
function render(){document.body.classList.toggle('home',selected===null);renderNav();updateCounts();if(selected===null)renderHome();else{const p=DATA.pages.find(p=>p.id===selected);$('article').innerHTML=p?'<h1>'+escape(p.title)+badge(p.change)+(p.candidate_id&&state(p)!=='pending'?badge(state(p)):'')+'</h1>'+(p.previousPath?'<p class="note">'+escape(p.previousPath)+' → '+escape(p.path)+'</p>':'')+(p.candidate_id?p.html:'<div class="unchanged">'+t('unchanged')+'</div>')+(p.sources.length?'<details><summary>'+(language==='zh'?'来源引用':'Sources')+'</summary><ul>'+p.sources.map(s=>'<li>'+escape(s)+'</li>').join('')+'</ul></details>':''):''}controls();localizeBody()}
|
|
104446
|
+
function setDecision(id,value){const current=decisions.get(id);if(current===value){decisions.delete(id);notes.delete(id)}else if(!current){if(value==='revised'){$('revision-note').focus();return}decisions.set(id,value)}render()}
|
|
104447
|
+
function setAllDecision(value){for(const p of candidates)if(!decisions.has(p.candidate_id))decisions.set(p.candidate_id,value);render()}
|
|
104448
|
+
function payloadText(){const statuses=ordered.map(p=>state(p));return feedbackCodec.encode({scope:SCOPE.label,idsHash:SCOPE.ids_sha256,contentHash:SCOPE.candidates_sha256,baselineHash:DATA.baselineHash,statuses,repairs:ordered.flatMap((p,index)=>state(p)==='revised'?[{index,instruction:notes.get(p.candidate_id)}]:[])})}
|
|
104449
|
+
async function copyPayload(){dismissGuide();const c=totals();$('copy-warning').textContent='';$('payload').value='';if(c.pending===candidates.length){$('copy-title').textContent=t('noSelection');$('payload').value=''}else{try{const text=payloadText();$('payload').value=text;await navigator.clipboard.writeText(text);$('copy-title').textContent=t('copied')}catch(e){$('copy-title').textContent=t('failed');if(!$('payload').value)$('payload').value=String(e.message)}}$('copy-summary').textContent=t('approved')+' '+c.approved+' · '+t('rejected')+' '+c.rejected+' · '+t('revised')+' '+c.revised+' · '+t('notReviewed')+' '+c.pending;$('copy-instructions').textContent=t('instructions');$('copy-warning').textContent=Array.from($('payload').value).length>1000?t('long'):'';$('copy-dialog').showModal()}
|
|
104450
|
+
let guideTimer;function dismissGuide(){clearInterval(guideTimer);$('copy-guide').hidden=true}
|
|
104451
|
+
function labels(){$('all-approved').textContent=t('allApprove');$('all-rejected').textContent=t('allReject');$('payload-open').textContent=t('copy');$('guide-text').textContent=t('guide');$('guide-close').textContent=t('known');$('bulk-title').textContent=t('allApprove');$('bulk-message').textContent=t('confirmAll');$('bulk-cancel').textContent=t('cancel');$('bulk-confirm').textContent=t('confirm');$('payload-close').textContent=t('close');$('language').textContent=language==='zh'?'EN':'中文'}
|
|
104452
|
+
document.addEventListener('click',e=>{const b=e.target.closest('button');if(!b)return;if(b.dataset.page){showPage(b.dataset.page);window.scrollTo(0,0)}if(b.dataset.root){active=b.dataset.root;const n=DATA.nodes.find(n=>n.key===active);selected=n?.page||[...descendants(active)].find(d=>d.page)?.page||null;render()}if(b.dataset.node){const n=DATA.nodes.find(n=>n.key===b.dataset.node);if(n.page)showPage(n.page);else{expanded.has(n.key)?expanded.delete(n.key):expanded.add(n.key);renderNav()}}});
|
|
104453
|
+
$('home').onclick=()=>{selected=null;active=null;render();window.scrollTo(0,0)};
|
|
104454
|
+
$('revision-note').oninput=e=>{const p=DATA.pages.find(p=>p.id===selected);if(!p?.candidate_id)return;const value=e.target.value;notes.set(p.candidate_id,value);if(value.trim())decisions.set(p.candidate_id,'revised');else decisions.delete(p.candidate_id);controls();renderNav();updateCounts();$('article').querySelectorAll('h1 > .approved,h1 > .revised,h1 > .rejected').forEach(e=>e.remove());$('article').querySelector('h1')?.insertAdjacentHTML('beforeend',value.trim()?badge('revised'):'')};
|
|
104455
|
+
for(const [id,value]of[['revise-btn','revised'],['reject-btn','rejected'],['approve-btn','approved']])$(id).onclick=()=>{const p=DATA.pages.find(p=>p.id===selected);if(p?.candidate_id)setDecision(p.candidate_id,value)};
|
|
104456
|
+
const addedRoots=DATA.nodes.filter(n=>!n.parent&&n.change==='new'&&!n.removed&&n.key!=='review-unplaced');
|
|
104457
|
+
let bulkTimer,bulkDeadline=0;
|
|
104458
|
+
function updateBulkConfirmation(){const remaining=addedRoots.length?Math.max(0,Math.ceil((bulkDeadline-Date.now())/1000)):0;$('bulk-confirm').disabled=addedRoots.length>0&&(!$('bulk-ack').checked||remaining>0);$('bulk-confirm').textContent=t('confirm')+(remaining?' ('+remaining+'s)':'');return remaining}
|
|
104459
|
+
function openBulkConfirmation(){clearInterval(bulkTimer);$('bulk-ack').checked=false;$('bulk-roots').hidden=!addedRoots.length;$('bulk-roots-title').textContent=t('newRoots');$('bulk-roots-list').innerHTML=addedRoots.map(n=>'<li>'+escape(nodeTitle(n))+'</li>').join('');$('bulk-ack-label').textContent=t('ackRoots');bulkDeadline=Date.now()+8000;updateBulkConfirmation();$('bulk-dialog').showModal();if(addedRoots.length)bulkTimer=setInterval(()=>{if(!updateBulkConfirmation())clearInterval(bulkTimer)},200)}
|
|
104460
|
+
$('bulk-ack').onchange=updateBulkConfirmation;
|
|
104461
|
+
$('bulk-dialog').onclose=()=>clearInterval(bulkTimer);
|
|
104462
|
+
$('all-approved').onclick=openBulkConfirmation;$('bulk-confirm').onclick=()=>{updateBulkConfirmation();if(!$('bulk-dialog').open||$('bulk-confirm').disabled)return;setAllDecision('approved');clearInterval(bulkTimer);$('bulk-dialog').close()};$('bulk-cancel').onclick=()=>{clearInterval(bulkTimer);$('bulk-dialog').close()};$('all-rejected').onclick=()=>setAllDecision('rejected');$('payload-open').onclick=copyPayload;$('payload-close').onclick=()=>$('copy-dialog').close();$('guide-close').onclick=dismissGuide;$('theme').onclick=()=>document.body.classList.toggle('dark');$('language').onclick=()=>{language=language==='zh'?'en':'zh';labels();render()};
|
|
104463
|
+
labels();render();const deadline=Date.now()+10000;guideTimer=setInterval(()=>{const left=Math.max(0,Math.ceil((deadline-Date.now())/1000));$('guide-countdown').textContent=left+'s';if(!left)dismissGuide()},250);
|
|
104464
|
+
`;
|
|
104465
|
+
|
|
104466
|
+
// src/project/reviewSiteStyles.ts
|
|
104467
|
+
var REVIEW_SITE_STYLES = String.raw`
|
|
104468
|
+
:root{--blue:#2563eb;--text:#161e2e;--muted:#646b7c;--line:#e7e9ef;--bg:#fff;--side:#f8f9fc;--red:#c63848;--amber:#a4660a}*{box-sizing:border-box}body{margin:0;color:var(--text);background:var(--bg);font:14px/1.75 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",sans-serif}button,input,textarea{font:inherit}button{cursor:pointer}button:disabled{opacity:.35;cursor:not-allowed}[hidden]{display:none!important}header{height:64px;padding:0 24px;display:flex;align-items:center;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5}#home{border:0;background:none;color:var(--text);width:280px;flex-shrink:0;text-align:left;font-size:15px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nav{display:flex;flex:1;min-width:0;overflow:auto;align-self:stretch}nav button{border:0;background:none;color:var(--text);padding:0 16px;font-size:13px;font-weight:550;white-space:nowrap}nav .active{box-shadow:inset 0 -2px var(--blue)}.new,nav .new{color:var(--red)}.modify,nav .modify{color:var(--amber)}.badge{display:inline-block;margin-left:7px;padding:1px 6px;font-size:10px;line-height:18px;vertical-align:middle;border-radius:4px;font-weight:600;letter-spacing:0}.badge.new{background:#fff0f1}.badge.modify,.badge.revised{background:#fff4dd;color:var(--amber)}.badge.approved{background:#e9f7ef;color:#258451}.badge.rejected{background:#f4edf0;color:#9e4458}.tools{display:flex;align-items:center;gap:8px;position:relative;margin-left:12px}.btn{border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--text);padding:5px 10px;font-size:12px;line-height:22px;white-space:nowrap}.primary{color:white;background:var(--blue);border-color:var(--blue)}#theme,#language{margin-left:8px;flex-shrink:0}.counter{position:relative;padding:8px;white-space:nowrap;font-size:12px;font-weight:700;color:var(--blue)}.counter-pop{display:none;position:absolute;top:100%;right:0;min-width:320px;padding:15px;background:var(--bg);border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 30px #17244220;color:var(--text);font-weight:400}.counter:hover .counter-pop,.counter:focus .counter-pop{display:block}.counter-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;font-size:11px}.counter-grid b{display:block;font-size:20px}.counter-pop p{border-top:1px solid var(--line);padding-top:10px}.layout{display:grid;grid-template-columns:280px minmax(0,1fr)}aside{height:calc(100vh - 64px);position:sticky;top:64px;overflow:auto;background:var(--side);border-right:1px solid var(--line);padding:12px 0 80px}.node{display:block;width:100%;text-align:left;border:0;background:none;min-height:32px;padding:6px 16px;font-size:13px;font-weight:450;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--muted)}.node.new{color:var(--red)}.node.modify{color:var(--amber)}.node.selected{background:#2563eb14}.caret{float:right}.node:hover{background:#8e96aa1a}main{min-width:0;padding:44px 60px 100px 36px}h1{font-size:36px;line-height:1.3;letter-spacing:-.025em;margin:0 0 28px}h2{font-size:25px;line-height:1.4;font-weight:650;margin:46px 0 22px;border-bottom:1px solid var(--line);padding-bottom:14px}h3{font-size:19px;margin:30px 0 14px}article{font-size:16px;line-height:1.8}article>h1>.badge{margin-left:12px}article a{color:inherit}article a:hover{color:var(--blue)}article table{border-collapse:collapse;font-size:14px;display:block;overflow:auto}article th,article td{border:1px solid var(--line);padding:11px 14px}article th{background:var(--side)}pre{overflow:auto;background:var(--side);padding:16px}blockquote{padding:16px 20px;margin:24px 0;border-left:3px solid #00bec8;background:#2563eb14;color:var(--muted)}.home .layout{display:block}.home aside{display:none}.home main{width:1120px;max-width:calc(100% - 280px);margin-left:280px;padding-top:32px}.home article{font-size:14px}.home h1{font-size:28px;margin-bottom:12px}.home h2{font-size:19px;margin:24px 0 12px;padding-bottom:10px}.home h3{font-size:15px;margin:16px 0 10px}.stats{font-size:12px;color:var(--muted)}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:8px}.cards button{border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--text);text-align:left;font-size:13px;line-height:20px;padding:10px 14px}.cards button:hover{border-color:var(--blue)}.unchanged{border:1px dashed var(--line);border-radius:8px;padding:24px;text-align:center;color:var(--muted);font-size:13px}.changed{position:relative;border-left:3px solid #e8b051;background:#fffaf0;color:#8f5607;padding:24px 18px 12px;margin:20px 0}.changed>.badge{position:absolute;right:12px;top:6px}.changed details{color:var(--muted)}.workspace-tree{font:12px/1.9 ui-monospace,monospace;border:1px solid var(--line);border-radius:8px;padding:16px;background:var(--side);overflow:auto}.workspace-tree button{border:0;background:none;color:var(--text);padding:0;font:inherit;white-space:nowrap}.note{font-size:12px;color:var(--muted)}footer{position:fixed;bottom:0;left:280px;right:0;background:var(--bg);border-top:1px solid var(--line);padding:12px 35px;display:flex;gap:10px;z-index:4}footer input{flex:1;min-width:0;border:0;outline:0;background:transparent;color:var(--text);font-size:14px;padding:8px 0}footer .btn{min-width:76px}.chosen{color:var(--blue);border-color:var(--blue);background:var(--bg)}.hover-label{display:none}.chosen:hover .normal{display:none}.chosen:hover .hover-label{display:inline}dialog{width:560px;max-width:90vw;background:var(--bg);color:var(--text);border:1px solid var(--line);border-radius:10px;padding:20px;font-size:13px;line-height:1.65}dialog::backdrop{background:#131b3255}dialog h2{font-size:17px;margin:0 0 12px;border:0;padding:0}dialog textarea{width:100%;height:130px;border:1px solid var(--line);border-radius:6px;padding:10px;font:11px/1.6 monospace;background:var(--side);color:var(--text)}#copy-warning{color:var(--amber)}.bulk-ack{display:flex;align-items:center;gap:8px;font-size:13px}.bulk-ack input{accent-color:var(--blue)}#bulk-roots-list{margin:8px 0 12px;padding-left:22px;color:var(--red)}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}.guide{position:absolute;right:0;top:calc(100% + 18px);width:300px;background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:15px;box-shadow:0 10px 35px #17244224;font-size:12px}.guide:before{content:'';position:absolute;top:-7px;right:42px;width:12px;height:12px;background:var(--bg);border-top:1px solid var(--line);border-left:1px solid var(--line);transform:rotate(45deg)}#guide-countdown{float:right;color:var(--muted);font-size:11px}.dark{--bg:#171b24;--side:#1b1d24;--text:#e2e4ed;--muted:#a1a6b7;--line:#303440;--blue:#8eb7ff}.dark .changed{background:#302919;color:#f2cf85}@media(min-width:1600px){main,.home main{padding-left:52px}}@media(max-width:1250px){#home{width:190px}nav button{padding:0 8px}.tools{gap:4px}}@media(max-width:1050px){header{height:auto;flex-wrap:wrap;min-height:64px}nav{order:3;flex-basis:100%;height:44px}.tools{margin-left:auto}}@media(max-width:700px){.layout{display:block}aside{position:relative;top:0;height:200px}main,.home main{width:100%;max-width:100%;margin:0;padding:24px 20px 90px}footer{left:0;padding:10px;flex-wrap:wrap}footer input{flex-basis:100%}.tools{flex-wrap:wrap}h1{font-size:28px}}
|
|
104409
104469
|
`;
|
|
104410
104470
|
|
|
104411
104471
|
// src/project/reviewHtml.ts
|
|
104412
|
-
var REVIEW_HTML_ROOT =
|
|
104472
|
+
var REVIEW_HTML_ROOT = join87(".tmp", "context-runtime", "review");
|
|
104413
104473
|
async function collectReviewCandidates(projectRoot, collection) {
|
|
104414
104474
|
const rows = await readCandidateRecords(projectRoot);
|
|
104415
104475
|
const draftRows = rows.filter((row) => row.collection === collection && row.status === "draft");
|
|
@@ -104426,446 +104486,23 @@ async function collectAllReviewCandidates(projectRoot) {
|
|
|
104426
104486
|
snapshot: await readReviewCandidateSnapshot(projectRoot, record4)
|
|
104427
104487
|
})));
|
|
104428
104488
|
}
|
|
104429
|
-
function
|
|
104430
|
-
return value.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """);
|
|
104431
|
-
}
|
|
104432
|
-
function jsonForScript(value) {
|
|
104433
|
-
return JSON.stringify(value).replace(/</gu, "\\u003c").replace(/>/gu, "\\u003e").replace(/&/gu, "\\u0026").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029");
|
|
104434
|
-
}
|
|
104435
|
-
function renderReviewHtml(candidates, reviewScope) {
|
|
104436
|
-
const candidateIds = candidates.map(({ record: record4 }) => record4.candidate_id);
|
|
104437
|
-
const visibleCandidateIds = [...candidateIds].sort();
|
|
104489
|
+
function renderReviewHtml(candidates, reviewScope, model) {
|
|
104438
104490
|
const scope2 = {
|
|
104439
|
-
|
|
104440
|
-
|
|
104441
|
-
|
|
104442
|
-
|
|
104443
|
-
|
|
104444
|
-
|
|
104445
|
-
|
|
104446
|
-
|
|
104447
|
-
|
|
104448
|
-
|
|
104449
|
-
|
|
104450
|
-
article_id: record4.article_id,
|
|
104451
|
-
module: record4.module,
|
|
104452
|
-
status: record4.status,
|
|
104453
|
-
kind: record4.kind,
|
|
104454
|
-
visibility: record4.visibility,
|
|
104455
|
-
source_refs: record4.source_refs,
|
|
104456
|
-
source_paths: record4.indexer_candidate === undefined ? [] : [...new Set(record4.indexer_candidate.sections.flatMap((section) => section.references).map((binding) => binding.locator.path))].sort(),
|
|
104457
|
-
sections: record4.indexer_candidate.sections.map((section) => ({
|
|
104458
|
-
id: section.section_key,
|
|
104459
|
-
kind: record4.kind,
|
|
104460
|
-
summary: section.section_key,
|
|
104461
|
-
body: section.markdown,
|
|
104462
|
-
source_refs: [...new Set(section.references.map((reference2) => `${reference2.source_ref}/${reference2.locator.path}#L${reference2.locator.start_line}-L${reference2.locator.end_line}`))].sort(),
|
|
104463
|
-
content_mode: "authored"
|
|
104464
|
-
})),
|
|
104465
|
-
group_key: reviewScope === "all" ? `${record4.collection} / ${candidateGroupKey({ record: record4, snapshot })}` : candidateGroupKey({ record: record4, snapshot }),
|
|
104466
|
-
group_label: reviewScope === "all" ? `${record4.collection} · ${candidateGroupLabel({ record: record4, snapshot })}` : candidateGroupLabel({ record: record4, snapshot }),
|
|
104467
|
-
review: record4.review,
|
|
104468
|
-
display_summary: record4.review.behavior_summary ?? record4.review.summary,
|
|
104469
|
-
preview: candidatePreview({ record: record4, snapshot }),
|
|
104470
|
-
rendered_markdown: renderReviewMarkdown(record4.indexer_candidate.sections.map((section) => section.markdown).join(`
|
|
104471
|
-
|
|
104472
|
-
`), record4.review.title),
|
|
104473
|
-
snapshot_ready: snapshot !== undefined
|
|
104474
|
-
};
|
|
104475
|
-
});
|
|
104476
|
-
return `<!doctype html>
|
|
104477
|
-
<html lang="en" data-theme="light">
|
|
104478
|
-
<head>
|
|
104479
|
-
<meta charset="utf-8">
|
|
104480
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
104481
|
-
<title>Context Review - ${escapeHtml(reviewScope)}</title>
|
|
104482
|
-
<style>${REVIEW_HTML_STYLES}</style>
|
|
104483
|
-
</head>
|
|
104484
|
-
<body>
|
|
104485
|
-
<main class="shell">
|
|
104486
|
-
<header class="header">
|
|
104487
|
-
<div class="titleline">
|
|
104488
|
-
<h1 id="review-heading">Context Review</h1>
|
|
104489
|
-
<div class="subtle" id="count-state">${candidates.length} draft candidate(s) in ${escapeHtml(reviewScope)} · ${candidates.length} pending 0 approved 0 omitted</div>
|
|
104490
|
-
</div>
|
|
104491
|
-
<div class="toolbar">
|
|
104492
|
-
<span class="bulk-actions">
|
|
104493
|
-
<button class="btn" id="all-approved">All approved</button>
|
|
104494
|
-
<button class="btn" id="all-rejected">Omit all</button>
|
|
104495
|
-
</span>
|
|
104496
|
-
<button class="btn brand" id="payload-open">Copy review results</button>
|
|
104497
|
-
<button class="btn language-btn" id="language" type="button" aria-label="Switch to Chinese">中文</button>
|
|
104498
|
-
<button class="btn icon-btn" id="theme" title="Toggle theme" aria-label="Toggle theme">\uD83C\uDF19</button>
|
|
104499
|
-
</div>
|
|
104500
|
-
</header>
|
|
104501
|
-
<section class="layout">
|
|
104502
|
-
<aside class="panel candidate-panel">
|
|
104503
|
-
<div class="panel-head candidate-head">
|
|
104504
|
-
<span id="pages-label">Pages to review</span>
|
|
104505
|
-
<div class="filters" id="filters" aria-label="candidate filters">
|
|
104506
|
-
<label class="filter"><input id="filter-approved" type="checkbox" checked> <span id="label-approved">approved</span></label>
|
|
104507
|
-
<label class="filter"><input id="filter-rejected" type="checkbox" checked> <span id="label-rejected">omitted</span></label>
|
|
104508
|
-
<label class="filter"><input id="filter-pending" type="checkbox" checked> <span id="label-pending">pending</span></label>
|
|
104509
|
-
</div>
|
|
104510
|
-
</div>
|
|
104511
|
-
<input id="search" type="search" placeholder="Search pages or modules" aria-label="Search pages or modules">
|
|
104512
|
-
<div id="list"></div>
|
|
104513
|
-
</aside>
|
|
104514
|
-
<section class="panel detail-panel">
|
|
104515
|
-
<div class="panel-head reader-navigation"><span id="content-label">Page content</span><div><button class="btn" id="previous-page">Previous</button> <button class="btn" id="next-page">Next</button> <button class="btn" id="next-pending">Next pending</button></div></div>
|
|
104516
|
-
<div class="detail" id="detail"></div>
|
|
104517
|
-
</section>
|
|
104518
|
-
</section>
|
|
104519
|
-
<div class="modal hidden" id="payload-modal" role="dialog" aria-modal="true" aria-labelledby="payload-title">
|
|
104520
|
-
<section class="modal-card">
|
|
104521
|
-
<div>
|
|
104522
|
-
<h2 id="payload-title">Review results</h2>
|
|
104523
|
-
<div class="subtle" id="code-help">These choices take effect only after you send the review code back to the conversation. Each segment is at most 980 characters. Send every segment before applying.</div>
|
|
104524
|
-
</div>
|
|
104525
|
-
<div class="modal-body">
|
|
104526
|
-
<div id="decision-summary"></div>
|
|
104527
|
-
<div class="code-navigation" id="code-navigation" hidden><button class="btn" id="code-previous">Previous segment</button><span id="code-length"></span><button class="btn" id="code-next">Next segment</button></div>
|
|
104528
|
-
<textarea id="payload" aria-label="review code" readonly></textarea>
|
|
104529
|
-
</div>
|
|
104530
|
-
<div class="modal-actions">
|
|
104531
|
-
<span class="subtle" id="modal-copy-state"></span>
|
|
104532
|
-
<button class="btn" id="payload-close">Close</button>
|
|
104533
|
-
<button class="btn primary" id="payload-copy">Copy</button>
|
|
104534
|
-
</div>
|
|
104535
|
-
</section>
|
|
104536
|
-
</div>
|
|
104537
|
-
</main>
|
|
104538
|
-
<script>
|
|
104539
|
-
const translations = ${jsonForScript(REVIEW_UI_ZH)};
|
|
104540
|
-
let language = /^zh(?:-|$)/i.test(navigator.languages?.[0] || navigator.language || "en") ? "zh-CN" : "en";
|
|
104541
|
-
function t(message, values = {}) {
|
|
104542
|
-
const text = language === "zh-CN" ? translations[message] || message : message;
|
|
104543
|
-
return text.replace(/\\{(\\w+)\\}/g, (_, key) => String(values[key] ?? ""));
|
|
104544
|
-
}
|
|
104545
|
-
const candidates = ${jsonForScript(candidateData)};
|
|
104546
|
-
const payloadScope = ${jsonForScript(scope2)};
|
|
104547
|
-
const reviewCode = (${createReviewCodeCodec.toString()})();
|
|
104548
|
-
const payloadScopeLabel = ${jsonForScript(reviewScope)};
|
|
104549
|
-
const decisions = new Map(candidates.map((item) => [item.candidate_id, "pending"]));
|
|
104550
|
-
const list = document.getElementById("list");
|
|
104551
|
-
const detail = document.getElementById("detail");
|
|
104552
|
-
const countState = document.getElementById("count-state");
|
|
104553
|
-
const filterApproved = document.getElementById("filter-approved");
|
|
104554
|
-
const filterRejected = document.getElementById("filter-rejected");
|
|
104555
|
-
const filterPending = document.getElementById("filter-pending");
|
|
104556
|
-
const theme = document.getElementById("theme");
|
|
104557
|
-
const allApproved = document.getElementById("all-approved");
|
|
104558
|
-
const allRejected = document.getElementById("all-rejected");
|
|
104559
|
-
const payloadOpen = document.getElementById("payload-open");
|
|
104560
|
-
const payloadModal = document.getElementById("payload-modal");
|
|
104561
|
-
const payloadClose = document.getElementById("payload-close");
|
|
104562
|
-
const payloadCopy = document.getElementById("payload-copy");
|
|
104563
|
-
const payloadBox = document.getElementById("payload");
|
|
104564
|
-
const modalCopyState = document.getElementById("modal-copy-state");
|
|
104565
|
-
const search = document.getElementById("search");
|
|
104566
|
-
const decisionSummary = document.getElementById("decision-summary");
|
|
104567
|
-
const codeLength = document.getElementById("code-length");
|
|
104568
|
-
let codePart = 0;
|
|
104569
|
-
let selected = candidates[0]?.candidate_id;
|
|
104570
|
-
const collapsedGroups = new Set();
|
|
104571
|
-
|
|
104572
|
-
function html(value) {
|
|
104573
|
-
return String(value).replace(/[&<>"]/g, (char) => ({ "&":"&", "<":"<", ">":">", '"':""" }[char]));
|
|
104574
|
-
}
|
|
104575
|
-
function decisionCounts() {
|
|
104576
|
-
const counts = { pending: 0, approved: 0, rejected: 0 };
|
|
104577
|
-
for (const status of decisions.values()) counts[status] += 1;
|
|
104578
|
-
return counts;
|
|
104579
|
-
}
|
|
104580
|
-
function updateCountState() {
|
|
104581
|
-
const counts = decisionCounts();
|
|
104582
|
-
countState.textContent = t("{count} pages · {scope} · {pending} pending · {approved} approved · {rejected} omitted",
|
|
104583
|
-
{ count: candidates.length, scope: payloadScopeLabel === "all" ? t("All collections") : payloadScopeLabel, ...counts });
|
|
104584
|
-
}
|
|
104585
|
-
function visibleCandidates() {
|
|
104586
|
-
const showApproved = filterApproved.checked;
|
|
104587
|
-
const showRejected = filterRejected.checked;
|
|
104588
|
-
const showPending = filterPending.checked;
|
|
104589
|
-
const query = search.value.trim().toLowerCase();
|
|
104590
|
-
return candidates.filter((item) => {
|
|
104591
|
-
if (query && !(item.review.title + " " + item.module + " " + item.source_paths.join(" ")).toLowerCase().includes(query)) return false;
|
|
104592
|
-
const status = decisions.get(item.candidate_id);
|
|
104593
|
-
return (status === "pending" && showPending) ||
|
|
104594
|
-
(status === "approved" && showApproved) ||
|
|
104595
|
-
(status === "rejected" && showRejected);
|
|
104596
|
-
});
|
|
104597
|
-
}
|
|
104598
|
-
function groupCandidates(items) {
|
|
104599
|
-
const groups = [];
|
|
104600
|
-
const byGroup = new Map();
|
|
104601
|
-
for (const item of items) {
|
|
104602
|
-
const key = item.group_key || item.module || "ungrouped";
|
|
104603
|
-
let group = byGroup.get(key);
|
|
104604
|
-
if (!group) {
|
|
104605
|
-
group = { key, label: item.group_label || key, items: [] };
|
|
104606
|
-
byGroup.set(key, group);
|
|
104607
|
-
groups.push(group);
|
|
104608
|
-
}
|
|
104609
|
-
group.items.push(item);
|
|
104610
|
-
}
|
|
104611
|
-
return groups;
|
|
104612
|
-
}
|
|
104613
|
-
function statusBadge(status) {
|
|
104614
|
-
const label = t(status === "rejected" ? "omitted" : status);
|
|
104615
|
-
return '<span class="badge ' + html(status) + '">' + html(label) + '</span>';
|
|
104616
|
-
}
|
|
104617
|
-
function toggleGroup(groupKey) {
|
|
104618
|
-
if (collapsedGroups.has(groupKey)) collapsedGroups.delete(groupKey);
|
|
104619
|
-
else collapsedGroups.add(groupKey);
|
|
104620
|
-
render();
|
|
104621
|
-
}
|
|
104622
|
-
function setGroupDecision(groupKey, status) {
|
|
104623
|
-
const items = candidates.filter((item) => (item.group_key || item.module || "ungrouped") === groupKey);
|
|
104624
|
-
if (items.length === 0) return;
|
|
104625
|
-
const label = t(status === "rejected" ? "omitted" : status);
|
|
104626
|
-
if (!window.confirm(t("Set all {count} pages in {group} to {status}?", { count: items.length, group: groupKey, status: label }))) return;
|
|
104627
|
-
for (const item of items) {
|
|
104628
|
-
if (status === "approved" && !item.snapshot_ready) continue;
|
|
104629
|
-
decisions.set(item.candidate_id, status);
|
|
104630
|
-
}
|
|
104631
|
-
codePart = 0;
|
|
104632
|
-
modalCopyState.textContent = t("Choices changed. Copy the updated code before applying.");
|
|
104633
|
-
render();
|
|
104634
|
-
updatePayloadBox();
|
|
104635
|
-
}
|
|
104636
|
-
function setAllDecision(status) {
|
|
104637
|
-
if (candidates.length === 0) return;
|
|
104638
|
-
const label = t(status === "rejected" ? "omitted" : status);
|
|
104639
|
-
if (!window.confirm(t("Set all {count} pages to {status}?", { count: candidates.length, status: label }))) return;
|
|
104640
|
-
for (const item of candidates) {
|
|
104641
|
-
if (status === "approved" && !item.snapshot_ready) continue;
|
|
104642
|
-
decisions.set(item.candidate_id, status);
|
|
104643
|
-
}
|
|
104644
|
-
codePart = 0;
|
|
104645
|
-
modalCopyState.textContent = t("Choices changed. Copy the updated code before applying.");
|
|
104646
|
-
render();
|
|
104647
|
-
updatePayloadBox();
|
|
104648
|
-
}
|
|
104649
|
-
function payloadParts() {
|
|
104650
|
-
if (decisionCounts().pending === candidates.length) return [];
|
|
104651
|
-
const ordered = [...candidates].sort((a, b) => a.candidate_id < b.candidate_id ? -1 : a.candidate_id > b.candidate_id ? 1 : 0);
|
|
104652
|
-
return reviewCode.encode(payloadScopeLabel, payloadScope.ids_sha256, payloadScope.candidates_sha256,
|
|
104653
|
-
ordered.map((item) => decisions.get(item.candidate_id)));
|
|
104654
|
-
}
|
|
104655
|
-
function payloadText() { return payloadParts()[codePart] || t("Select at least one page decision; pending pages remain for later review."); }
|
|
104656
|
-
function setDecision(id, status) {
|
|
104657
|
-
const item = candidates.find((candidate) => candidate.candidate_id === id);
|
|
104658
|
-
if (status === "approved" && item && !item.snapshot_ready) return;
|
|
104659
|
-
decisions.set(id, status);
|
|
104660
|
-
codePart = 0;
|
|
104661
|
-
modalCopyState.textContent = t("Choices changed. Copy the updated code before applying.");
|
|
104662
|
-
render();
|
|
104663
|
-
updatePayloadBox();
|
|
104664
|
-
}
|
|
104665
|
-
function updatePayloadBox() {
|
|
104666
|
-
const parts = payloadParts();
|
|
104667
|
-
codePart = Math.min(codePart, Math.max(0, parts.length - 1));
|
|
104668
|
-
payloadBox.value = payloadText();
|
|
104669
|
-
document.getElementById("code-navigation").hidden = parts.length <= 1;
|
|
104670
|
-
codeLength.textContent = parts.length ? t("Segment {part}/{total} · {length}/980 characters", { part: codePart + 1, total: parts.length, length: payloadBox.value.length }) : t("No review code yet");
|
|
104671
|
-
document.getElementById("code-previous").disabled = codePart === 0;
|
|
104672
|
-
document.getElementById("code-next").disabled = codePart + 1 >= parts.length;
|
|
104673
|
-
const counts = decisionCounts();
|
|
104674
|
-
const ready = counts.approved + counts.rejected > 0;
|
|
104675
|
-
decisionSummary.innerHTML = '<p>' + html(t('{approved} approved · {rejected} not included · {pending} pending', counts)) + '</p>' +
|
|
104676
|
-
(counts.rejected ? '<details><summary>' + html(t('Pages not included')) + '</summary><ul>' + candidates.filter((item) => decisions.get(item.candidate_id) === "rejected").map((item) => '<li>' + html(item.review.title) + '</li>').join('') + '</ul></details>' : '');
|
|
104677
|
-
payloadCopy.disabled = !ready;
|
|
104678
|
-
payloadOpen.classList.toggle("ready", ready);
|
|
104679
|
-
payloadOpen.title = ready
|
|
104680
|
-
? t("Open review results")
|
|
104681
|
-
: t("{count} pending pages remain", { count: counts.pending });
|
|
104682
|
-
}
|
|
104683
|
-
function openPayloadModal() {
|
|
104684
|
-
updatePayloadBox();
|
|
104685
|
-
payloadModal.classList.remove("hidden");
|
|
104686
|
-
payloadBox.focus();
|
|
104687
|
-
payloadBox.select();
|
|
104688
|
-
modalCopyState.textContent = "";
|
|
104689
|
-
}
|
|
104690
|
-
function closePayloadModal() {
|
|
104691
|
-
payloadModal.classList.add("hidden");
|
|
104692
|
-
}
|
|
104693
|
-
async function copyPayload() {
|
|
104694
|
-
const counts = decisionCounts();
|
|
104695
|
-
if (counts.approved + counts.rejected === 0) {
|
|
104696
|
-
updatePayloadBox();
|
|
104697
|
-
const message = t("Select at least one page decision; pending pages remain for later review.");
|
|
104698
|
-
modalCopyState.textContent = message;
|
|
104699
|
-
return;
|
|
104700
|
-
}
|
|
104701
|
-
const text = payloadText();
|
|
104702
|
-
payloadBox.value = text;
|
|
104703
|
-
try {
|
|
104704
|
-
if (!navigator.clipboard) throw new Error("clipboard unavailable");
|
|
104705
|
-
await navigator.clipboard.writeText(text);
|
|
104706
|
-
modalCopyState.textContent = t("Copied");
|
|
104707
|
-
} catch {
|
|
104708
|
-
payloadBox.focus();
|
|
104709
|
-
payloadBox.select();
|
|
104710
|
-
modalCopyState.textContent = t("Copy manually from the textarea");
|
|
104711
|
-
}
|
|
104712
|
-
}
|
|
104713
|
-
function render() {
|
|
104714
|
-
updateCountState();
|
|
104715
|
-
if (candidates.length === 0) {
|
|
104716
|
-
list.innerHTML = '<div class="empty">' + html(t('No draft candidates.')) + '</div>';
|
|
104717
|
-
detail.innerHTML = '<div class="empty">' + html(t('Nothing to review.')) + '</div>';
|
|
104718
|
-
return;
|
|
104719
|
-
}
|
|
104720
|
-
const visible = visibleCandidates();
|
|
104721
|
-
if (visible.length === 0) {
|
|
104722
|
-
list.innerHTML = '<div class="empty">' + html(t('No candidates match the current filters.')) + '</div>';
|
|
104723
|
-
detail.innerHTML = '<div class="empty">' + html(t('Adjust the candidate filters to continue reviewing.')) + '</div>';
|
|
104724
|
-
return;
|
|
104725
|
-
}
|
|
104726
|
-
if (!visible.some((item) => item.candidate_id === selected)) selected = visible[0].candidate_id;
|
|
104727
|
-
list.innerHTML = groupCandidates(visible).map((group) =>
|
|
104728
|
-
{
|
|
104729
|
-
const collapsed = collapsedGroups.has(group.key);
|
|
104730
|
-
return '<section class="candidate-group">' +
|
|
104731
|
-
'<div class="candidate-group-title" data-group-toggle="' + html(group.key) + '">' +
|
|
104732
|
-
'<span class="group-label"><span>' + (collapsed ? "▸" : "▾") + '</span><span class="group-key">' + html(group.label) + '</span><span class="group-count">' + group.items.length + ' items</span></span>' +
|
|
104733
|
-
'<span class="group-actions">' +
|
|
104734
|
-
'<button class="group-btn" data-group-status="approved" data-group="' + html(group.key) + '">' + html(t('All approved')) + '</button>' +
|
|
104735
|
-
'<button class="group-btn" data-group-status="rejected" data-group="' + html(group.key) + '">' + html(t('Omit all')) + '</button>' +
|
|
104736
|
-
'</span>' +
|
|
104737
|
-
'</div>' +
|
|
104738
|
-
(collapsed ? "" : group.items.map((item) => {
|
|
104739
|
-
const active = item.candidate_id === selected ? " active" : "";
|
|
104740
|
-
const status = decisions.get(item.candidate_id);
|
|
104741
|
-
return '<button class="candidate' + active + '" data-id="' + html(item.candidate_id) + '">' +
|
|
104742
|
-
'<div class="candidate-title">' +
|
|
104743
|
-
'<span class="candidate-title-text">' + html(item.review.title) + '</span>' +
|
|
104744
|
-
'<span class="candidate-tags"><span class="badge">' + html(item.collection || "unknown") + '</span>' + (!item.snapshot_ready ? '<span class="badge warning">' + html(t('evidence unavailable')) + '</span>' : '') + statusBadge(status) + '</span>' +
|
|
104745
|
-
'</div>' +
|
|
104746
|
-
'<div class="candidate-summary">' + html(item.display_summary || item.review.summary) + '</div>' +
|
|
104747
|
-
'</button>';
|
|
104748
|
-
}).join("")) +
|
|
104749
|
-
'</section>';
|
|
104750
|
-
}
|
|
104751
|
-
).join("");
|
|
104752
|
-
const item = visible.find((candidate) => candidate.candidate_id === selected) ?? visible[0];
|
|
104753
|
-
selected = item.candidate_id;
|
|
104754
|
-
const status = decisions.get(item.candidate_id);
|
|
104755
|
-
const evidenceWarning = item.snapshot_ready ? "" :
|
|
104756
|
-
'<div class="notice warning">' + html(t('Source snapshot unavailable. Restore it before approving this candidate, or omit the page.')) + '</div>';
|
|
104757
|
-
const sectionDetails = '<article class="reader-body">' + item.rendered_markdown + '</article>';
|
|
104758
|
-
const previewBlock = '<details class="technical-details"><summary>' + html(t('Source Markdown')) + '</summary><pre>' +
|
|
104759
|
-
html(item.sections.map((section) => section.body).join("\\n\\n")) + '</pre></details>';
|
|
104760
|
-
const displayedSources = [...new Set([...item.source_paths, ...item.source_refs, ...item.sections.flatMap((section) => section.source_refs)])];
|
|
104761
|
-
const sourceLocationsBlock = displayedSources.length === 0 ? "" :
|
|
104762
|
-
'<details class="technical-details">' +
|
|
104763
|
-
'<summary>' + html(t('Source locations')) + '(' + displayedSources.length + ')</summary>' +
|
|
104764
|
-
'<div class="technical-content"><div class="section-source-refs">' +
|
|
104765
|
-
displayedSources.map((ref) => '<code>' + html(ref) + '</code>').join('') +
|
|
104766
|
-
'</div></div>' +
|
|
104767
|
-
'</details>';
|
|
104768
|
-
detail.innerHTML = '<div class="detail-titlebar">' +
|
|
104769
|
-
'<div class="page-location">' + html(item.path) + '</div>' +
|
|
104770
|
-
'<div class="actions">' +
|
|
104771
|
-
'<button class="btn approve ' + (status === "approved" ? "active" : "") + '" data-action="approved" ' + (!item.snapshot_ready ? "disabled" : "") + '>' + html(t('Approve')) + '</button>' +
|
|
104772
|
-
'<button class="btn reject ' + (status === "rejected" ? "active" : "") + '" data-action="rejected">' + html(t('Omit')) + '</button>' +
|
|
104773
|
-
'</div>' +
|
|
104774
|
-
'</div>' +
|
|
104775
|
-
evidenceWarning +
|
|
104776
|
-
sectionDetails +
|
|
104777
|
-
previewBlock +
|
|
104778
|
-
'<p class="repair-hint">' + html(t('Need changes? Leave this page pending and ask the agent to repair it. Other reviewed pages can be approved.')) + '</p>' +
|
|
104779
|
-
sourceLocationsBlock;
|
|
104780
|
-
document.querySelectorAll("[data-id]").forEach((button) => button.addEventListener("click", () => { selected = button.dataset.id; render(); }));
|
|
104781
|
-
document.querySelectorAll("[data-action]").forEach((button) => button.addEventListener("click", () => setDecision(item.candidate_id, button.dataset.action)));
|
|
104782
|
-
document.querySelectorAll("[data-group-toggle]").forEach((header) => header.addEventListener("click", () => toggleGroup(header.dataset.groupToggle)));
|
|
104783
|
-
document.querySelectorAll("[data-group-status]").forEach((button) => button.addEventListener("click", (event) => {
|
|
104784
|
-
event.stopPropagation();
|
|
104785
|
-
setGroupDecision(button.dataset.group, button.dataset.groupStatus);
|
|
104786
|
-
}));
|
|
104787
|
-
}
|
|
104788
|
-
function navigatePage(direction, pendingOnly = false) {
|
|
104789
|
-
const items = visibleCandidates();
|
|
104790
|
-
const current = items.findIndex((item) => item.candidate_id === selected);
|
|
104791
|
-
for (let step = 1; step <= items.length; step++) {
|
|
104792
|
-
const item = items[(current + direction * step + items.length * 2) % items.length];
|
|
104793
|
-
if (!pendingOnly || decisions.get(item.candidate_id) === "pending") { selected = item.candidate_id; render(); detail.scrollTop = 0; return; }
|
|
104794
|
-
}
|
|
104795
|
-
}
|
|
104796
|
-
search.addEventListener("input", render);
|
|
104797
|
-
document.getElementById("previous-page").addEventListener("click", () => navigatePage(-1));
|
|
104798
|
-
document.getElementById("next-page").addEventListener("click", () => navigatePage(1));
|
|
104799
|
-
document.getElementById("next-pending").addEventListener("click", () => navigatePage(1, true));
|
|
104800
|
-
document.getElementById("code-previous").addEventListener("click", () => { codePart = Math.max(0, codePart - 1); updatePayloadBox(); });
|
|
104801
|
-
document.getElementById("code-next").addEventListener("click", () => { codePart++; updatePayloadBox(); });
|
|
104802
|
-
function applyLanguage() {
|
|
104803
|
-
document.documentElement.lang = language;
|
|
104804
|
-
document.title = t("Context Review") + " - " + payloadScopeLabel;
|
|
104805
|
-
const labels = {
|
|
104806
|
-
"review-heading": "Context Review", "all-approved": "All approved", "all-rejected": "Omit all",
|
|
104807
|
-
"payload-open": "Copy review results", "pages-label": "Pages to review", "label-approved": "approved",
|
|
104808
|
-
"label-rejected": "omitted", "label-pending": "pending", "content-label": "Page content",
|
|
104809
|
-
"previous-page": "Previous", "next-page": "Next", "next-pending": "Next pending",
|
|
104810
|
-
"payload-title": "Review results", "code-previous": "Previous segment", "code-next": "Next segment",
|
|
104811
|
-
"payload-close": "Close", "payload-copy": "Copy",
|
|
104812
|
-
"code-help": "These choices take effect only after you send the review code back to the conversation. Each segment is at most 980 characters. Send every segment before applying.",
|
|
104813
|
-
};
|
|
104814
|
-
for (const [id, message] of Object.entries(labels)) document.getElementById(id).textContent = t(message);
|
|
104815
|
-
search.placeholder = t("Search pages or modules");
|
|
104816
|
-
search.setAttribute("aria-label", t("Search pages or modules"));
|
|
104817
|
-
document.getElementById("filters").setAttribute("aria-label", t("candidate filters"));
|
|
104818
|
-
payloadBox.setAttribute("aria-label", t("review code"));
|
|
104819
|
-
theme.title = t("Toggle theme");
|
|
104820
|
-
theme.setAttribute("aria-label", t("Toggle theme"));
|
|
104821
|
-
const button = document.getElementById("language");
|
|
104822
|
-
button.textContent = language === "zh-CN" ? "English" : "中文";
|
|
104823
|
-
button.setAttribute("aria-label", language === "zh-CN" ? "Switch to English" : "切换为中文");
|
|
104824
|
-
modalCopyState.textContent = "";
|
|
104825
|
-
const scrollTop = detail.scrollTop;
|
|
104826
|
-
render();
|
|
104827
|
-
detail.scrollTop = scrollTop;
|
|
104828
|
-
updatePayloadBox();
|
|
104829
|
-
}
|
|
104830
|
-
function toggleLanguage() {
|
|
104831
|
-
language = language === "zh-CN" ? "en" : "zh-CN";
|
|
104832
|
-
applyLanguage();
|
|
104833
|
-
}
|
|
104834
|
-
document.getElementById("language").addEventListener("click", toggleLanguage);
|
|
104835
|
-
function effectiveTheme() {
|
|
104836
|
-
return document.documentElement.dataset.theme || "light";
|
|
104837
|
-
}
|
|
104838
|
-
function updateThemeIcon() {
|
|
104839
|
-
theme.textContent = effectiveTheme() === "dark" ? "☀️" : "\uD83C\uDF19";
|
|
104840
|
-
}
|
|
104841
|
-
theme.addEventListener("click", () => {
|
|
104842
|
-
document.documentElement.dataset.theme = effectiveTheme() === "dark" ? "light" : "dark";
|
|
104843
|
-
updateThemeIcon();
|
|
104844
|
-
});
|
|
104845
|
-
updateThemeIcon();
|
|
104846
|
-
allApproved.addEventListener("click", () => setAllDecision("approved"));
|
|
104847
|
-
allRejected.addEventListener("click", () => setAllDecision("rejected"));
|
|
104848
|
-
filterApproved.addEventListener("change", render);
|
|
104849
|
-
filterRejected.addEventListener("change", render);
|
|
104850
|
-
filterPending.addEventListener("change", render);
|
|
104851
|
-
payloadOpen.addEventListener("click", openPayloadModal);
|
|
104852
|
-
payloadClose.addEventListener("click", closePayloadModal);
|
|
104853
|
-
payloadCopy.addEventListener("click", copyPayload);
|
|
104854
|
-
payloadModal.addEventListener("click", (event) => {
|
|
104855
|
-
if (event.target === payloadModal) closePayloadModal();
|
|
104856
|
-
});
|
|
104857
|
-
document.addEventListener("keydown", (event) => {
|
|
104858
|
-
if (event.key === "Escape" && !payloadModal.classList.contains("hidden")) closePayloadModal();
|
|
104859
|
-
});
|
|
104860
|
-
applyLanguage();
|
|
104861
|
-
</script>
|
|
104862
|
-
</body>
|
|
104863
|
-
</html>
|
|
104864
|
-
`;
|
|
104491
|
+
label: reviewScope,
|
|
104492
|
+
ids_sha256: candidateIdsHash(candidates.map((c) => c.record.candidate_id).sort()),
|
|
104493
|
+
candidates_sha256: candidateSetHash(candidates.map((c) => c.record))
|
|
104494
|
+
};
|
|
104495
|
+
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeReviewHtml(model.title)} · Review</title><style>${REVIEW_SITE_STYLES}</style></head><body>
|
|
104496
|
+
<header><button id="home">${escapeReviewHtml(model.title)}</button><nav id="top"></nav><div class="tools"><div class="counter" tabindex="0"><span id="counts"></span><div class="counter-pop" id="counter-pop"></div></div><button class="btn" id="all-approved"></button><button class="btn" id="all-rejected"></button><button class="btn primary" id="payload-open"></button><div class="guide" id="copy-guide"><span id="guide-countdown">10s</span><p id="guide-text"></p><button class="btn" id="guide-close"></button></div></div><button class="btn" id="theme" aria-label="Theme">◐</button><button class="btn" id="language"></button></header>
|
|
104497
|
+
<div class="layout"><aside id="tree"></aside><main><article id="article"></article></main></div>
|
|
104498
|
+
<footer id="footer" hidden><input id="revision-note" aria-label="Revision instructions"><button class="btn" id="revise-btn"></button><button class="btn" id="reject-btn"></button><button class="btn primary" id="approve-btn"></button></footer>
|
|
104499
|
+
<dialog id="bulk-dialog"><h2 id="bulk-title"></h2><p id="bulk-message"></p><div id="bulk-roots" hidden><strong id="bulk-roots-title"></strong><ul id="bulk-roots-list"></ul><label class="bulk-ack"><input type="checkbox" id="bulk-ack"><span id="bulk-ack-label"></span></label></div><div class="dialog-actions"><button class="btn" id="bulk-cancel"></button><button class="btn primary" id="bulk-confirm"></button></div></dialog>
|
|
104500
|
+
<dialog id="copy-dialog"><h2 id="copy-title"></h2><p id="copy-summary"></p><p id="copy-instructions"></p><textarea id="payload" readonly aria-label="Review code"></textarea><p id="copy-warning"></p><button class="btn" id="payload-close"></button></dialog>
|
|
104501
|
+
<script>const DATA=${reviewHtmlJson(model)};const SCOPE=${reviewHtmlJson(scope2)};const feedbackCodec=(${createReviewFeedbackCodec.toString()})();${REVIEW_SITE_CLIENT}</script></body></html>`;
|
|
104865
104502
|
}
|
|
104866
104503
|
function resolveOutputPath(projectRoot, outPath, reviewScope) {
|
|
104867
104504
|
if (outPath === undefined)
|
|
104868
|
-
return
|
|
104505
|
+
return join87(projectRoot, REVIEW_HTML_ROOT, `${reviewScope}.html`);
|
|
104869
104506
|
return isAbsolute15(outPath) ? outPath : resolve28(projectRoot, outPath);
|
|
104870
104507
|
}
|
|
104871
104508
|
async function writeReviewHtml(input) {
|
|
@@ -104875,8 +104512,8 @@ async function writeReviewHtml(input) {
|
|
|
104875
104512
|
}
|
|
104876
104513
|
const candidates = reviewScope === "all" ? await collectAllReviewCandidates(input.projectRoot) : await collectReviewCandidates(input.projectRoot, reviewScope);
|
|
104877
104514
|
const outPath = resolveOutputPath(input.projectRoot, input.out, reviewScope);
|
|
104878
|
-
await mkdir30(
|
|
104879
|
-
await writeFile24(outPath, renderReviewHtml(candidates, reviewScope), "utf8");
|
|
104515
|
+
await mkdir30(dirname37(outPath), { recursive: true });
|
|
104516
|
+
await writeFile24(outPath, renderReviewHtml(candidates, reviewScope, await collectReviewSiteModel(input.projectRoot, candidates)), "utf8");
|
|
104880
104517
|
return {
|
|
104881
104518
|
path: outPath,
|
|
104882
104519
|
candidates: candidates.length,
|
|
@@ -104889,8 +104526,8 @@ async function writeReviewHtml(input) {
|
|
|
104889
104526
|
init_productionRequirements();
|
|
104890
104527
|
init_dist();
|
|
104891
104528
|
init_atomicWrite();
|
|
104892
|
-
import { mkdir as mkdir31, readFile as
|
|
104893
|
-
import { join as
|
|
104529
|
+
import { mkdir as mkdir31, readFile as readFile69 } from "node:fs/promises";
|
|
104530
|
+
import { join as join88 } from "node:path";
|
|
104894
104531
|
var REVIEW_BATCH_MAX_CANDIDATES = 6;
|
|
104895
104532
|
var REVIEW_BATCH_MAX_BYTES = 512 * 1024;
|
|
104896
104533
|
async function readerPurposes(projectRoot, sources) {
|
|
@@ -104966,15 +104603,16 @@ function buildCurrentReviewBatchDocuments(candidates) {
|
|
|
104966
104603
|
});
|
|
104967
104604
|
}
|
|
104968
104605
|
async function materializeCurrentReviewBatchSet(input) {
|
|
104606
|
+
const feedback = await readPendingReviewFeedback(input.projectRoot, input.candidates);
|
|
104969
104607
|
const batches = buildCurrentReviewBatchDocuments(input.candidates);
|
|
104970
104608
|
const setDigest = digestText(batches.map((batch) => `${batch.task_key}:${batch.digest}`).join(`
|
|
104971
104609
|
`));
|
|
104972
|
-
const root2 =
|
|
104610
|
+
const root2 = join88(input.projectRoot, ".tmp", "context-runtime", "review", `current-${setDigest.slice("sha256:".length)}`);
|
|
104973
104611
|
await mkdir31(root2, { recursive: true });
|
|
104974
104612
|
const entries2 = [];
|
|
104975
104613
|
for (const batch of batches) {
|
|
104976
|
-
const path4 =
|
|
104977
|
-
const existing = await
|
|
104614
|
+
const path4 = join88(input.projectRoot, ".tmp", "context-runtime", "review", `${batch.task_key}-${batch.digest.slice("sha256:".length)}.md`);
|
|
104615
|
+
const existing = await readFile69(path4, "utf8").catch((error) => {
|
|
104978
104616
|
if (error.code === "ENOENT")
|
|
104979
104617
|
return;
|
|
104980
104618
|
throw error;
|
|
@@ -104986,6 +104624,7 @@ async function materializeCurrentReviewBatchSet(input) {
|
|
|
104986
104624
|
}
|
|
104987
104625
|
const content3 = [
|
|
104988
104626
|
"# Current knowledge Review",
|
|
104627
|
+
...feedback.length ? ["", "## Pending user revision instructions", ...feedback.map((item) => JSON.stringify(item)), "Apply these through their repair commands, then review the new candidates. Do not approve unchanged drafts to bypass feedback."] : [],
|
|
104989
104628
|
"",
|
|
104990
104629
|
"## Reader purposes",
|
|
104991
104630
|
"",
|
|
@@ -105026,7 +104665,7 @@ async function materializeCurrentReviewBatchSet(input) {
|
|
|
105026
104665
|
].join(`
|
|
105027
104666
|
`);
|
|
105028
104667
|
const digest6 = digestText(content3);
|
|
105029
|
-
const path3 =
|
|
104668
|
+
const path3 = join88(root2, "index.md");
|
|
105030
104669
|
await atomicWriteFile(path3, `${content3}
|
|
105031
104670
|
`);
|
|
105032
104671
|
return {
|
|
@@ -105059,11 +104698,11 @@ function shellQuote6(value) {
|
|
|
105059
104698
|
}
|
|
105060
104699
|
function receiptSetPath(receipts) {
|
|
105061
104700
|
const token = digestText(JSON.stringify(receipts)).slice("sha256:".length);
|
|
105062
|
-
return
|
|
104701
|
+
return join89(".tmp", "context-runtime", "workflow", "read-receipts", `${token}.json`);
|
|
105063
104702
|
}
|
|
105064
104703
|
async function writeReceiptContinuation(input) {
|
|
105065
104704
|
const path3 = receiptSetPath(input.receipts);
|
|
105066
|
-
const absolutePath =
|
|
104705
|
+
const absolutePath = join89(input.projectRoot, path3);
|
|
105067
104706
|
await writeJsonAtomic(absolutePath, input.receipts);
|
|
105068
104707
|
const contextCommand = input.managed ? [
|
|
105069
104708
|
"context",
|
|
@@ -105158,7 +104797,7 @@ async function materializeContextWorkflowResource(input) {
|
|
|
105158
104797
|
const resourceId = workflowResourceId(input.resourceId);
|
|
105159
104798
|
const content3 = renderContextWorkflowResource(resourceId, status);
|
|
105160
104799
|
const location = await materializeResource(await loadContextWorkflowProvider(), resourceId, {
|
|
105161
|
-
cache:
|
|
104800
|
+
cache: join89(found.projectRoot, ".tmp", "context-runtime", "workflow", "resources"),
|
|
105162
104801
|
workspace: found.projectRoot,
|
|
105163
104802
|
revision: input.revision,
|
|
105164
104803
|
input: {
|
|
@@ -105190,7 +104829,7 @@ async function materializeContextWorkflowResource(input) {
|
|
|
105190
104829
|
receipts: afterReadReceipts
|
|
105191
104830
|
});
|
|
105192
104831
|
const directResources = (status.workflow.current?.resources.required ?? []).filter((resource) => resource.read_state === "read-required" && resource.path !== undefined && resource.digest !== undefined);
|
|
105193
|
-
const afterReadCommand = directResources.length === 0 ? continuation.command : `context resource acknowledge-current --revision ${shellQuote6(input.revision)}${authorityCommandOptions(authorities, "resource")} --resource-receipts ${shellQuote6(`@${
|
|
104832
|
+
const afterReadCommand = directResources.length === 0 ? continuation.command : `context resource acknowledge-current --revision ${shellQuote6(input.revision)}${authorityCommandOptions(authorities, "resource")} --resource-receipts ${shellQuote6(`@${join89(found.projectRoot, continuation.path)}`)} --format json`;
|
|
105194
104833
|
return {
|
|
105195
104834
|
protocol: "context.workflow.resource.v1",
|
|
105196
104835
|
id: resourceId,
|
|
@@ -105251,14 +104890,14 @@ async function acknowledgeCurrentWorkflowResources(input) {
|
|
|
105251
104890
|
const reevaluated = await reevaluateProjectStatusWorkflow({
|
|
105252
104891
|
snapshot,
|
|
105253
104892
|
resourceReceipts: normalizedReceipts,
|
|
105254
|
-
resourceReceiptsReference: `@${
|
|
104893
|
+
resourceReceiptsReference: `@${join89(found.projectRoot, continuation.path)}`
|
|
105255
104894
|
});
|
|
105256
104895
|
return {
|
|
105257
104896
|
...reevaluated,
|
|
105258
104897
|
resourceAcknowledgement: {
|
|
105259
104898
|
protocol: "context.workflow.resource-receipts.v1",
|
|
105260
104899
|
acknowledged: directResources.length,
|
|
105261
|
-
receiptReference: `@${
|
|
104900
|
+
receiptReference: `@${join89(found.projectRoot, continuation.path)}`
|
|
105262
104901
|
}
|
|
105263
104902
|
};
|
|
105264
104903
|
}
|
|
@@ -105304,9 +104943,9 @@ init_cliFeedback();
|
|
|
105304
104943
|
init_errors3();
|
|
105305
104944
|
init_exitCode();
|
|
105306
104945
|
init_workspace();
|
|
105307
|
-
import { readFile as
|
|
105308
|
-
import { isAbsolute as isAbsolute16, join as
|
|
105309
|
-
var RECEIPT_DIRECTORY =
|
|
104946
|
+
import { readFile as readFile70 } from "node:fs/promises";
|
|
104947
|
+
import { isAbsolute as isAbsolute16, join as join90, sep as sep6, resolve as resolve29 } from "node:path";
|
|
104948
|
+
var RECEIPT_DIRECTORY = join90(".tmp", "context-runtime", "workflow", "read-receipts");
|
|
105310
104949
|
function workflowResourceReceiptCwd(value, cwd) {
|
|
105311
104950
|
if (value === undefined || !value.startsWith("@"))
|
|
105312
104951
|
return cwd;
|
|
@@ -105324,7 +104963,7 @@ async function receiptDocument(value, cwd) {
|
|
|
105324
104963
|
let source2 = value;
|
|
105325
104964
|
if (value.startsWith("@")) {
|
|
105326
104965
|
try {
|
|
105327
|
-
source2 = await
|
|
104966
|
+
source2 = await readFile70(resolve29(cwd, value.slice(1)), "utf8");
|
|
105328
104967
|
} catch (error) {
|
|
105329
104968
|
const ioCode = error !== null && typeof error === "object" && "code" in error && typeof error.code === "string" ? error.code : undefined;
|
|
105330
104969
|
throw new ContextError(ExitCode.UserError, "resource read receipt file is unavailable", {
|
|
@@ -105485,33 +105124,33 @@ function runSuccessBaseBody(input) {
|
|
|
105485
105124
|
`log: ${input.logPath}`
|
|
105486
105125
|
];
|
|
105487
105126
|
}
|
|
105488
|
-
function appendCaptureFileRunBody(
|
|
105127
|
+
function appendCaptureFileRunBody(body2, result) {
|
|
105489
105128
|
const nextAction = nextActionCommand(result.next_action);
|
|
105490
|
-
|
|
105129
|
+
body2.push(`source: file:${result.source.name}`, `include: ${result.source.include.join(", ")}`, `documents: ${result.documents.length}`, `snapshot: ${result.snapshot.manifest}`, `snapshot hash: ${result.snapshot.snapshot_hash}`, `changed: ${result.snapshot.changed ? "yes" : "no"}`);
|
|
105491
105130
|
if (nextAction !== undefined)
|
|
105492
|
-
|
|
105131
|
+
body2.push(`next action: ${nextAction}`);
|
|
105493
105132
|
for (const document4 of result.documents.slice(0, 8)) {
|
|
105494
|
-
|
|
105133
|
+
body2.push(`document ${document4.path}: ${document4.title} (${document4.line_count} line(s))`);
|
|
105495
105134
|
}
|
|
105496
105135
|
}
|
|
105497
|
-
function appendCaptureLarkRunBody(
|
|
105136
|
+
function appendCaptureLarkRunBody(body2, result) {
|
|
105498
105137
|
const nextAction = nextActionCommand(result.next_action);
|
|
105499
|
-
|
|
105138
|
+
body2.push(`source: lark:${result.source.name}`, `identity: ${result.source.identity}`, `documents: ${result.documents.length}`, `assets: ${result.assets.length}`, `snapshot: ${result.snapshot.manifest}`, `snapshot hash: ${result.snapshot.snapshot_hash}`, `changed: ${result.snapshot.changed ? "yes" : "no"}`);
|
|
105500
105139
|
if (nextAction !== undefined)
|
|
105501
|
-
|
|
105140
|
+
body2.push(`next action: ${nextAction}`);
|
|
105502
105141
|
for (const document4 of result.documents.slice(0, 8)) {
|
|
105503
|
-
|
|
105142
|
+
body2.push(`document ${document4.path}: ${document4.title} (${document4.line_count} line(s))`);
|
|
105504
105143
|
}
|
|
105505
105144
|
}
|
|
105506
|
-
function appendRunResultBody(
|
|
105145
|
+
function appendRunResultBody(body2, result) {
|
|
105507
105146
|
if (isCaptureFileRunResult(result))
|
|
105508
|
-
appendCaptureFileRunBody(
|
|
105147
|
+
appendCaptureFileRunBody(body2, result);
|
|
105509
105148
|
if (isCaptureLarkRunResult(result))
|
|
105510
|
-
appendCaptureLarkRunBody(
|
|
105149
|
+
appendCaptureLarkRunBody(body2, result);
|
|
105511
105150
|
if (result !== null && typeof result === "object" && !Array.isArray(result) && "kind" in result && (result.kind === "semantic.rules.view.result" || result.kind === "diagnostics.view.result") && "next_action" in result && result.next_action !== null && typeof result.next_action === "object" && !Array.isArray(result.next_action)) {
|
|
105512
105151
|
const nextCommand = nextActionCommand(result.next_action);
|
|
105513
105152
|
if (nextCommand !== undefined)
|
|
105514
|
-
|
|
105153
|
+
body2.push(`next action: ${nextCommand}`);
|
|
105515
105154
|
}
|
|
105516
105155
|
}
|
|
105517
105156
|
function writeRunSuccess(input) {
|
|
@@ -105532,14 +105171,14 @@ function writeRunSuccess(input) {
|
|
|
105532
105171
|
`);
|
|
105533
105172
|
return;
|
|
105534
105173
|
}
|
|
105535
|
-
const
|
|
105536
|
-
appendRunResultBody(
|
|
105174
|
+
const body2 = runSuccessBaseBody(input);
|
|
105175
|
+
appendRunResultBody(body2, input.result);
|
|
105537
105176
|
process.stdout.write(formatFeedback({
|
|
105538
105177
|
symbol: "✓",
|
|
105539
105178
|
action: "ran",
|
|
105540
105179
|
subject: input.plan.phase.id,
|
|
105541
105180
|
headline: input.plan.phase.kind,
|
|
105542
|
-
body
|
|
105181
|
+
body: body2
|
|
105543
105182
|
}));
|
|
105544
105183
|
}
|
|
105545
105184
|
function compactDiagnostics(record4) {
|
|
@@ -105571,15 +105210,15 @@ function compactJsonResult(result, verbose) {
|
|
|
105571
105210
|
// src/project/runLog.ts
|
|
105572
105211
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
105573
105212
|
import { mkdir as mkdir32, writeFile as writeFile25 } from "node:fs/promises";
|
|
105574
|
-
import { dirname as
|
|
105213
|
+
import { dirname as dirname38, join as join93 } from "node:path";
|
|
105575
105214
|
var createPhaseRunId = () => {
|
|
105576
105215
|
const timestamp = new Date().toISOString().replace(/[-:.TZ]/gu, "");
|
|
105577
105216
|
return `run_${timestamp}_${randomUUID5().slice(0, 8)}`;
|
|
105578
105217
|
};
|
|
105579
105218
|
async function writePhaseRunLog(input) {
|
|
105580
|
-
const relPath =
|
|
105581
|
-
const absPath =
|
|
105582
|
-
await mkdir32(
|
|
105219
|
+
const relPath = join93(".tmp", "context-runtime", "runs", `${input.runId}.json`);
|
|
105220
|
+
const absPath = join93(input.projectRoot, relPath);
|
|
105221
|
+
await mkdir32(dirname38(absPath), { recursive: true });
|
|
105583
105222
|
await writeFile25(absPath, `${JSON.stringify({
|
|
105584
105223
|
run_id: input.runId,
|
|
105585
105224
|
phase_id: input.phase.id,
|
|
@@ -105784,7 +105423,7 @@ function writeRunPlan(plan, format2, preview, previewError) {
|
|
|
105784
105423
|
`);
|
|
105785
105424
|
return;
|
|
105786
105425
|
}
|
|
105787
|
-
const
|
|
105426
|
+
const body2 = [
|
|
105788
105427
|
`dry-run: ${plan.dryRun ? "yes" : "no"}`,
|
|
105789
105428
|
`reads: ${plan.phase.reads.length > 0 ? plan.phase.reads.join(", ") : "none"}`,
|
|
105790
105429
|
`writes: ${plan.phase.writes.length > 0 ? plan.phase.writes.join(", ") : "none"}`,
|
|
@@ -105796,7 +105435,7 @@ function writeRunPlan(plan, format2, preview, previewError) {
|
|
|
105796
105435
|
action: "planned",
|
|
105797
105436
|
subject: plan.phase.id,
|
|
105798
105437
|
headline: plan.phase.kind,
|
|
105799
|
-
body
|
|
105438
|
+
body: body2
|
|
105800
105439
|
}));
|
|
105801
105440
|
}
|
|
105802
105441
|
function customPhaseContext(input) {
|
|
@@ -106318,7 +105957,7 @@ init_debugTrace();
|
|
|
106318
105957
|
// src/project/workflow/workflowExecutionRuntime.ts
|
|
106319
105958
|
init_src();
|
|
106320
105959
|
init_debugTrace();
|
|
106321
|
-
import { createHash as
|
|
105960
|
+
import { createHash as createHash28 } from "node:crypto";
|
|
106322
105961
|
import { spawn as spawn5 } from "node:child_process";
|
|
106323
105962
|
function digestText2(value, includeTail) {
|
|
106324
105963
|
const filtered = redactIndexerOutputText({
|
|
@@ -106328,7 +105967,7 @@ function digestText2(value, includeTail) {
|
|
|
106328
105967
|
const bytes = Buffer.byteLength(filtered);
|
|
106329
105968
|
return {
|
|
106330
105969
|
bytes,
|
|
106331
|
-
sha256:
|
|
105970
|
+
sha256: createHash28("sha256").update(filtered).digest("hex"),
|
|
106332
105971
|
...includeTail && filtered.length > 0 ? { tail: filtered.slice(-8192) } : {}
|
|
106333
105972
|
};
|
|
106334
105973
|
}
|
|
@@ -106521,11 +106160,150 @@ execution scope cleanup failed`, true)
|
|
|
106521
106160
|
// src/project/workflow/workflowInProcessActions.ts
|
|
106522
106161
|
init_workflowFacts();
|
|
106523
106162
|
|
|
106163
|
+
// src/project/reviewCode.ts
|
|
106164
|
+
function createReviewCodeCodec() {
|
|
106165
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
106166
|
+
function checksum(text9) {
|
|
106167
|
+
let crc = 4294967295;
|
|
106168
|
+
for (let i2 = 0;i2 < text9.length; i2++) {
|
|
106169
|
+
crc ^= text9.charCodeAt(i2);
|
|
106170
|
+
for (let bit = 0;bit < 8; bit++)
|
|
106171
|
+
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
106172
|
+
}
|
|
106173
|
+
return ((crc ^ 4294967295) >>> 0).toString(16).padStart(8, "0");
|
|
106174
|
+
}
|
|
106175
|
+
function pack(bytes) {
|
|
106176
|
+
let value = 0, bits = 0, result = "";
|
|
106177
|
+
for (const byte of bytes) {
|
|
106178
|
+
value = value << 8 | byte;
|
|
106179
|
+
bits += 8;
|
|
106180
|
+
while (bits >= 6) {
|
|
106181
|
+
bits -= 6;
|
|
106182
|
+
result += alphabet[value >>> bits & 63];
|
|
106183
|
+
}
|
|
106184
|
+
}
|
|
106185
|
+
if (bits)
|
|
106186
|
+
result += alphabet[value << 6 - bits & 63];
|
|
106187
|
+
return result;
|
|
106188
|
+
}
|
|
106189
|
+
function unpack(text9) {
|
|
106190
|
+
if (!/^[A-Za-z0-9_-]*$/.test(text9))
|
|
106191
|
+
throw new Error("Invalid review code encoding");
|
|
106192
|
+
let value = 0, bits = 0;
|
|
106193
|
+
const bytes = [];
|
|
106194
|
+
for (const char of text9) {
|
|
106195
|
+
value = value << 6 | alphabet.indexOf(char);
|
|
106196
|
+
bits += 6;
|
|
106197
|
+
if (bits >= 8) {
|
|
106198
|
+
bits -= 8;
|
|
106199
|
+
bytes.push(value >>> bits & 255);
|
|
106200
|
+
}
|
|
106201
|
+
}
|
|
106202
|
+
if (pack(bytes) !== text9)
|
|
106203
|
+
throw new Error("Noncanonical review code encoding");
|
|
106204
|
+
return bytes;
|
|
106205
|
+
}
|
|
106206
|
+
function hash4(text9) {
|
|
106207
|
+
if (!/^[a-f0-9]{64}$/.test(text9))
|
|
106208
|
+
throw new Error("Review requires a complete candidate digest");
|
|
106209
|
+
return pack(Array.from({ length: 32 }, (_, i2) => Number.parseInt(text9.slice(i2 * 2, i2 * 2 + 2), 16)));
|
|
106210
|
+
}
|
|
106211
|
+
function unhash(text9) {
|
|
106212
|
+
const bytes = unpack(text9);
|
|
106213
|
+
if (bytes.length !== 32)
|
|
106214
|
+
throw new Error("Invalid candidate digest");
|
|
106215
|
+
return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
106216
|
+
}
|
|
106217
|
+
function encode(scope2, idsHash, contentHash2, statuses) {
|
|
106218
|
+
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !statuses.length || statuses.length > 1e6 || statuses.some((status) => status !== "approved" && status !== "rejected" && status !== "pending")) {
|
|
106219
|
+
throw new Error("Select at least one review decision and keep undecided pages pending");
|
|
106220
|
+
}
|
|
106221
|
+
if (statuses.every((s) => s === "pending"))
|
|
106222
|
+
throw new Error("Select at least one review decision");
|
|
106223
|
+
const mode = statuses.every((s) => s === "approved") ? "a" : statuses.every((s) => s === "rejected") ? "r" : statuses.includes("pending") ? "p" : "b";
|
|
106224
|
+
const bytes = Array(Math.ceil(statuses.length / (mode === "p" ? 4 : 8))).fill(0);
|
|
106225
|
+
if (mode === "p")
|
|
106226
|
+
statuses.forEach((s, i2) => {
|
|
106227
|
+
bytes[i2 >> 2] |= (s === "approved" ? 1 : s === "rejected" ? 2 : 0) << i2 % 4 * 2;
|
|
106228
|
+
});
|
|
106229
|
+
if (mode === "b")
|
|
106230
|
+
statuses.forEach((s, i2) => {
|
|
106231
|
+
if (s === "rejected")
|
|
106232
|
+
bytes[i2 >> 3] |= 1 << i2 % 8;
|
|
106233
|
+
});
|
|
106234
|
+
const body2 = ["CR1", scope2, statuses.length, hash4(idsHash), hash4(contentHash2), mode, mode === "b" || mode === "p" ? pack(bytes) : ""].join(".");
|
|
106235
|
+
const code3 = `${body2}.${checksum(body2)}`;
|
|
106236
|
+
if (code3.length <= 980)
|
|
106237
|
+
return [code3];
|
|
106238
|
+
const total = Math.ceil(code3.length / 900);
|
|
106239
|
+
if (total > 200)
|
|
106240
|
+
throw new Error("Review decisions exceed 200 segments; use a smaller collection scope");
|
|
106241
|
+
const identity = checksum(code3);
|
|
106242
|
+
return Array.from({ length: total }, (_, i2) => `CRP1.${identity}.${i2 + 1}.${total}.${code3.slice(i2 * 900, (i2 + 1) * 900)}`);
|
|
106243
|
+
}
|
|
106244
|
+
function decode2(input) {
|
|
106245
|
+
if (input.length > 250000)
|
|
106246
|
+
throw new Error("Review code exceeds the supported size");
|
|
106247
|
+
const lines = input.trim().split(/\s+/);
|
|
106248
|
+
let code3 = lines[0];
|
|
106249
|
+
if (code3.startsWith("CRP1.")) {
|
|
106250
|
+
const parts = new Map;
|
|
106251
|
+
let identity = "", total = 0;
|
|
106252
|
+
for (const line of lines) {
|
|
106253
|
+
const match = /^CRP1\.([a-f0-9]{8})\.([1-9][0-9]*)\.([1-9][0-9]*)\.(.+)$/.exec(line);
|
|
106254
|
+
if (!match || line.length > 980)
|
|
106255
|
+
throw new Error("Invalid review code segment");
|
|
106256
|
+
const index2 = Number(match[2]), count2 = Number(match[3]);
|
|
106257
|
+
if (count2 > 200 || index2 > count2 || parts.has(index2) || total && (total !== count2 || identity !== match[1])) {
|
|
106258
|
+
throw new Error("Duplicate or mixed review code segments");
|
|
106259
|
+
}
|
|
106260
|
+
identity = match[1];
|
|
106261
|
+
total = count2;
|
|
106262
|
+
parts.set(index2, match[4]);
|
|
106263
|
+
}
|
|
106264
|
+
if (parts.size !== total)
|
|
106265
|
+
throw new Error(`Missing review code segments: received ${parts.size} of ${total}; collect all segments before applying`);
|
|
106266
|
+
code3 = Array.from({ length: total }, (_, i2) => parts.get(i2 + 1)).join("");
|
|
106267
|
+
if (checksum(code3) !== identity)
|
|
106268
|
+
throw new Error("Review code segment checksum mismatch");
|
|
106269
|
+
} else if (lines.length !== 1 || code3.length > 980)
|
|
106270
|
+
throw new Error("Copy each complete review code segment unchanged");
|
|
106271
|
+
const fields = code3.split(".");
|
|
106272
|
+
if (fields.length !== 8 || fields[0] !== "CR1" || checksum(fields.slice(0, 7).join(".")) !== fields[7]) {
|
|
106273
|
+
throw new Error("Review code is damaged or unsupported; copy it again from the report");
|
|
106274
|
+
}
|
|
106275
|
+
const [, scope2, countText, ids, content3, mode, data2] = fields;
|
|
106276
|
+
if (!/^[a-z][a-z0-9-]*$/.test(scope2) || !/^[1-9][0-9]*$/.test(countText))
|
|
106277
|
+
throw new Error("Invalid review scope");
|
|
106278
|
+
const count = Number(countText);
|
|
106279
|
+
if (count > 1e6 || !["a", "r", "b", "p"].includes(mode))
|
|
106280
|
+
throw new Error("Invalid review decisions");
|
|
106281
|
+
const bytes = unpack(data2);
|
|
106282
|
+
const perByte = mode === "p" ? 4 : 8;
|
|
106283
|
+
if (mode === "b" || mode === "p" ? bytes.length !== Math.ceil(count / perByte) || count % perByte !== 0 && bytes.at(-1) >>> count % perByte * (mode === "p" ? 2 : 1) !== 0 : data2 !== "") {
|
|
106284
|
+
throw new Error("Invalid review decision bitmap");
|
|
106285
|
+
}
|
|
106286
|
+
const statuses = Array.from({ length: count }, (_, i2) => {
|
|
106287
|
+
if (mode === "p") {
|
|
106288
|
+
const value = bytes[i2 >> 2] >>> i2 % 4 * 2 & 3;
|
|
106289
|
+
if (value === 3)
|
|
106290
|
+
throw new Error("Invalid pending review bitmap");
|
|
106291
|
+
return value === 1 ? "approved" : value === 2 ? "rejected" : "pending";
|
|
106292
|
+
}
|
|
106293
|
+
return mode === "r" || mode === "b" && bytes[i2 >> 3] & 1 << i2 % 8 ? "rejected" : "approved";
|
|
106294
|
+
});
|
|
106295
|
+
if (statuses.every((s) => s === "pending"))
|
|
106296
|
+
throw new Error("Review contains no decisions");
|
|
106297
|
+
return { scope: scope2, count, idsHash: unhash(ids), contentHash: unhash(content3), statuses };
|
|
106298
|
+
}
|
|
106299
|
+
return { encode, decode: decode2 };
|
|
106300
|
+
}
|
|
106301
|
+
|
|
106524
106302
|
// src/project/review.ts
|
|
106525
106303
|
init_cliFeedback();
|
|
106526
106304
|
init_errors3();
|
|
106527
106305
|
init_exitCode();
|
|
106528
|
-
import { readFile as
|
|
106306
|
+
import { readFile as readFile75 } from "node:fs/promises";
|
|
106529
106307
|
import { isAbsolute as isAbsolute18, resolve as resolve30 } from "node:path";
|
|
106530
106308
|
|
|
106531
106309
|
// src/project/reviewApply.ts
|
|
@@ -106538,8 +106316,8 @@ init_writeLock();
|
|
|
106538
106316
|
init_reviewApplyIndexer();
|
|
106539
106317
|
init_approvedKnowledgeSnapshots();
|
|
106540
106318
|
import { existsSync as existsSync25 } from "node:fs";
|
|
106541
|
-
import { readFile as
|
|
106542
|
-
import { join as
|
|
106319
|
+
import { readFile as readFile73 } from "node:fs/promises";
|
|
106320
|
+
import { join as join94 } from "node:path";
|
|
106543
106321
|
|
|
106544
106322
|
// src/project/reviewCandidateAuthority.ts
|
|
106545
106323
|
init_src2();
|
|
@@ -106636,7 +106414,7 @@ async function prepareApprovedPage(input) {
|
|
|
106636
106414
|
next: "Refresh the current production or article revision, then reopen Review before approval."
|
|
106637
106415
|
});
|
|
106638
106416
|
}
|
|
106639
|
-
const relPath =
|
|
106417
|
+
const relPath = join94("knowledge", input.record.path);
|
|
106640
106418
|
const existingView = findApprovedPageForArticleId(input.record.indexer_candidate.artifact_ref, input.approvedPageIndex);
|
|
106641
106419
|
const previousPath = input.record.approved_revision?.previous_path;
|
|
106642
106420
|
if (previousPath !== undefined && (!isSafeKnowledgeTargetPath(previousPath.split("/")[0], previousPath) || previousPath.includes("\\")))
|
|
@@ -106651,13 +106429,13 @@ async function prepareApprovedPage(input) {
|
|
|
106651
106429
|
next: "Resolve the approved page path migration explicitly before approving this candidate."
|
|
106652
106430
|
});
|
|
106653
106431
|
}
|
|
106654
|
-
const absPath =
|
|
106655
|
-
const existing = existsSync25(absPath) ? await
|
|
106432
|
+
const absPath = join94(input.projectRoot, relPath);
|
|
106433
|
+
const existing = existsSync25(absPath) ? await readFile73(absPath, "utf8") : undefined;
|
|
106656
106434
|
let previous3;
|
|
106657
106435
|
if (previousPath !== undefined) {
|
|
106658
106436
|
if (existing !== undefined || existingView?.relPath !== `knowledge/${previousPath}`)
|
|
106659
106437
|
throw new TypeError("Page move destination or original identity changed; refresh its revision.");
|
|
106660
|
-
previous3 = { path: `knowledge/${previousPath}`, content: await
|
|
106438
|
+
previous3 = { path: `knowledge/${previousPath}`, content: await readFile73(join94(input.projectRoot, "knowledge", previousPath), "utf8") };
|
|
106661
106439
|
}
|
|
106662
106440
|
if (input.record.approved_revision !== undefined) {
|
|
106663
106441
|
const base = previous3?.content ?? existing;
|
|
@@ -106704,7 +106482,7 @@ async function prepareApprovedPage(input) {
|
|
|
106704
106482
|
}
|
|
106705
106483
|
async function readProjectFileMaybe(projectRoot, relPath) {
|
|
106706
106484
|
try {
|
|
106707
|
-
return await
|
|
106485
|
+
return await readFile73(join94(projectRoot, relPath), "utf8");
|
|
106708
106486
|
} catch (error) {
|
|
106709
106487
|
if (error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
106710
106488
|
return;
|
|
@@ -106834,6 +106612,36 @@ async function applyReviewDecisions(input) {
|
|
|
106834
106612
|
const rows = await readCandidateRecords(input.projectRoot);
|
|
106835
106613
|
const nextRows = [...rows];
|
|
106836
106614
|
const decisions = expandReviewPayload(input.payload, rows);
|
|
106615
|
+
const scoped = rows.filter((r) => r.status === "draft" && (input.payload.scope?.kind === "all" || r.collection === input.payload.collection)).sort((a, b) => a.candidate_id < b.candidate_id ? -1 : 1);
|
|
106616
|
+
if (input.payload.baseline_hash && input.payload.baseline_hash !== await reviewSiteBaselineHash(input.projectRoot, scoped.map((r) => r.approved_revision?.previous_path ?? r.path))) {
|
|
106617
|
+
throw new ContextError(ExitCode.WorkspaceStateError, "Review navigation or approved baseline changed; generate a fresh report", {
|
|
106618
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
106619
|
+
code: "review-baseline-stale",
|
|
106620
|
+
next: "context review html --all --format json"
|
|
106621
|
+
});
|
|
106622
|
+
}
|
|
106623
|
+
const repairIds = new Set;
|
|
106624
|
+
const repairs = (input.payload.feedback_repairs ?? []).map((repair) => {
|
|
106625
|
+
const row = scoped[repair.index];
|
|
106626
|
+
if (!Number.isInteger(repair.index) || !row || repairIds.has(repair.index) || !repair.instruction.trim() || input.payload.encoded_statuses?.[repair.index] !== "pending")
|
|
106627
|
+
throw new Error("Invalid review revision request");
|
|
106628
|
+
repairIds.add(repair.index);
|
|
106629
|
+
const quote = (value) => "'" + value.replace(/'/gu, "'\\''") + "'";
|
|
106630
|
+
return {
|
|
106631
|
+
candidate_id: row.candidate_id,
|
|
106632
|
+
path: row.path,
|
|
106633
|
+
fingerprint: row.fingerprint,
|
|
106634
|
+
instruction: repair.instruction,
|
|
106635
|
+
command: `context revise ${quote(row.candidate_id)} --instruction ${quote(repair.instruction)} --format json`
|
|
106636
|
+
};
|
|
106637
|
+
});
|
|
106638
|
+
const feedbackPath = repairs.length ? `.tmp/context-runtime/review-feedback/${indexerProtocolDigest(input.payload).replace("sha256:", "")}.json` : undefined;
|
|
106639
|
+
const feedbackTarget = feedbackPath === undefined ? undefined : reviewFileTarget({
|
|
106640
|
+
path: feedbackPath,
|
|
106641
|
+
baseContent: await readProjectFileMaybe(input.projectRoot, feedbackPath),
|
|
106642
|
+
targetContent: JSON.stringify({ created_at: now, repairs }, null, 2) + `
|
|
106643
|
+
`
|
|
106644
|
+
});
|
|
106837
106645
|
const approvesAnyCandidate = decisions.some((decision) => decision.status === "approved");
|
|
106838
106646
|
const candidateAuthority = approvesAnyCandidate ? await loadReviewCandidateAuthority(input.projectRoot) : undefined;
|
|
106839
106647
|
const approvedPageIndex = approvesAnyCandidate ? await buildApprovedArticleIndex(input.projectRoot) : {
|
|
@@ -106897,7 +106705,7 @@ async function applyReviewDecisions(input) {
|
|
|
106897
106705
|
});
|
|
106898
106706
|
}
|
|
106899
106707
|
seenApprovedIds.set(approvedRef, row.candidate_id);
|
|
106900
|
-
const approvedPath =
|
|
106708
|
+
const approvedPath = join94("knowledge", row.path);
|
|
106901
106709
|
const previousPathCandidate = seenApprovedPaths.get(knowledgeTargetPathKey(approvedPath));
|
|
106902
106710
|
if (previousPathCandidate !== undefined) {
|
|
106903
106711
|
throw new ContextError(ExitCode.UserError, `multiple approved review decisions target the same knowledge path: ${approvedPath}`, {
|
|
@@ -106947,13 +106755,14 @@ async function applyReviewDecisions(input) {
|
|
|
106947
106755
|
for (const path3 of approvedPageIndex.byRelPath.keys()) {
|
|
106948
106756
|
if (pagesToWrite.some((page) => page.relPath === path3 || page.previous?.path === path3))
|
|
106949
106757
|
continue;
|
|
106950
|
-
const before = await
|
|
106758
|
+
const before = await readFile73(join94(input.projectRoot, path3), "utf8");
|
|
106951
106759
|
const local = path3.replace(/^knowledge\//u, "");
|
|
106952
106760
|
const after = moveKnowledgeLinkTargets2(before, local, local, moved);
|
|
106953
106761
|
navigationTargets.push(reviewFileTarget({ path: path3, baseContent: before, targetContent: after }));
|
|
106954
106762
|
}
|
|
106955
106763
|
}
|
|
106956
106764
|
const targets = [
|
|
106765
|
+
feedbackTarget,
|
|
106957
106766
|
await prepareApprovedKnowledgeSnapshotTarget({ projectRoot: input.projectRoot, pages: pagesToWrite, candidates: rows }),
|
|
106958
106767
|
...navigationTargets,
|
|
106959
106768
|
...pagesToWrite.flatMap((page) => page.previous === undefined ? [] : [reviewFileTarget({
|
|
@@ -106989,6 +106798,7 @@ async function applyReviewDecisions(input) {
|
|
|
106989
106798
|
}
|
|
106990
106799
|
return {
|
|
106991
106800
|
applied: decisions.length,
|
|
106801
|
+
...feedbackPath ? { repairs, feedback_path: feedbackPath } : {},
|
|
106992
106802
|
approved,
|
|
106993
106803
|
rejected,
|
|
106994
106804
|
unchanged,
|
|
@@ -107001,13 +106811,13 @@ async function applyReviewDecisions(input) {
|
|
|
107001
106811
|
}
|
|
107002
106812
|
|
|
107003
106813
|
// src/project/reviewMaintenance.ts
|
|
107004
|
-
import { readFile as
|
|
106814
|
+
import { readFile as readFile74, writeFile as writeFile26 } from "node:fs/promises";
|
|
107005
106815
|
init_writeLock();
|
|
107006
106816
|
init_verifyFrontmatter();
|
|
107007
106817
|
function deprecateApprovedPage(input) {
|
|
107008
106818
|
return withProjectWriteLock(input.projectRoot, "deprecate-article", async () => {
|
|
107009
106819
|
const page = await approvedPageForArticleId(input.projectRoot, input.viewRef);
|
|
107010
|
-
const original = await
|
|
106820
|
+
const original = await readFile74(page.path, "utf8");
|
|
107011
106821
|
const content3 = parseFrontmatterLoose(original).deprecated === true ? original : updateFrontmatter(original, (metadata) => ({ ...metadata, deprecated: true, timestamp: new Date().toISOString() }));
|
|
107012
106822
|
const changed = content3 !== original;
|
|
107013
106823
|
if (changed)
|
|
@@ -107026,13 +106836,13 @@ function deprecateApprovedPage(input) {
|
|
|
107026
106836
|
init_candidateLedger();
|
|
107027
106837
|
|
|
107028
106838
|
// src/project/localHtmlReport.ts
|
|
107029
|
-
import { execFile as
|
|
107030
|
-
import { isAbsolute as isAbsolute17, join as
|
|
106839
|
+
import { execFile as execFile11 } from "node:child_process";
|
|
106840
|
+
import { isAbsolute as isAbsolute17, join as join95 } from "node:path";
|
|
107031
106841
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
107032
|
-
import { promisify as
|
|
107033
|
-
var execFileAsync5 =
|
|
106842
|
+
import { promisify as promisify11 } from "node:util";
|
|
106843
|
+
var execFileAsync5 = promisify11(execFile11);
|
|
107034
106844
|
function htmlReportReference(input) {
|
|
107035
|
-
const absolutePath = isAbsolute17(input.path) ? input.path :
|
|
106845
|
+
const absolutePath = isAbsolute17(input.path) ? input.path : join95(input.projectRoot, input.path);
|
|
107036
106846
|
return {
|
|
107037
106847
|
format: "html",
|
|
107038
106848
|
path: input.path,
|
|
@@ -107204,7 +107014,7 @@ function parseReviewPayloadText(raw) {
|
|
|
107204
107014
|
async function readReviewPayloadFile(filePath2) {
|
|
107205
107015
|
let raw;
|
|
107206
107016
|
try {
|
|
107207
|
-
raw = await
|
|
107017
|
+
raw = await readFile75(filePath2, "utf8");
|
|
107208
107018
|
} catch (error) {
|
|
107209
107019
|
const message = error instanceof Error ? error.message : String(error);
|
|
107210
107020
|
throw new ContextError(ExitCode.UserError, `review payload file cannot be read: ${filePath2}`, {
|
|
@@ -107216,6 +107026,24 @@ async function readReviewPayloadFile(filePath2) {
|
|
|
107216
107026
|
}
|
|
107217
107027
|
if (raw.trim().startsWith("CR")) {
|
|
107218
107028
|
try {
|
|
107029
|
+
if (raw.trim().startsWith("CR2.")) {
|
|
107030
|
+
const feedback = createReviewFeedbackCodec().decode(raw);
|
|
107031
|
+
const collection2 = feedback.scope === "all" ? undefined : assertCollection(feedback.scope);
|
|
107032
|
+
return {
|
|
107033
|
+
decisions: [],
|
|
107034
|
+
encoded_statuses: feedback.statuses.map((s) => s === "revised" ? "pending" : s),
|
|
107035
|
+
feedback_repairs: feedback.repairs,
|
|
107036
|
+
baseline_hash: feedback.baselineHash,
|
|
107037
|
+
...collection2 === undefined ? {} : { collection: collection2 },
|
|
107038
|
+
scope: {
|
|
107039
|
+
kind: collection2 === undefined ? "all" : "collection",
|
|
107040
|
+
...collection2 === undefined ? {} : { collection: collection2 },
|
|
107041
|
+
count: feedback.statuses.length,
|
|
107042
|
+
ids_sha256: feedback.idsHash,
|
|
107043
|
+
candidates_sha256: feedback.contentHash
|
|
107044
|
+
}
|
|
107045
|
+
};
|
|
107046
|
+
}
|
|
107219
107047
|
const decoded = createReviewCodeCodec().decode(raw);
|
|
107220
107048
|
const collection = decoded.scope === "all" ? undefined : assertCollection(decoded.scope);
|
|
107221
107049
|
return {
|
|
@@ -107233,7 +107061,7 @@ async function readReviewPayloadFile(filePath2) {
|
|
|
107233
107061
|
} catch (error) {
|
|
107234
107062
|
throw new ContextError(ExitCode.UserError, error instanceof Error ? error.message : String(error), {
|
|
107235
107063
|
category: ErrorCategory.UserInputInvalid,
|
|
107236
|
-
next: "Copy
|
|
107064
|
+
next: "Copy the complete review code and all following revision instruction lines unchanged from the current report into one input file. Older segmented codes require every segment."
|
|
107237
107065
|
});
|
|
107238
107066
|
}
|
|
107239
107067
|
}
|
|
@@ -107253,6 +107081,7 @@ function formatApplyResult(result, format2) {
|
|
|
107253
107081
|
`rejected: ${result.rejected}`,
|
|
107254
107082
|
`materialized: ${result.materialized}`,
|
|
107255
107083
|
`removed: ${result.removed}`,
|
|
107084
|
+
...(result.repairs ?? []).map((r) => `repair ${r.path}: ${r.command}`),
|
|
107256
107085
|
`unchanged: ${result.unchanged}`,
|
|
107257
107086
|
`candidate file: ${result.candidateFileUpdated ? "updated" : "unchanged"}`,
|
|
107258
107087
|
...result.pages.map((page) => `page: ${page}`),
|
|
@@ -107499,6 +107328,7 @@ async function runReviewApproveAllCommand(input) {
|
|
|
107499
107328
|
`materialized: ${result.materialized}`,
|
|
107500
107329
|
`unchanged: ${result.unchanged}`,
|
|
107501
107330
|
`removed: ${result.removed}`,
|
|
107331
|
+
...(result.repairs ?? []).map((r) => `repair ${r.path}: ${r.command}`),
|
|
107502
107332
|
...continuation === undefined ? [] : [`workflow: ${continuation.state}`, continuation.stop.message]
|
|
107503
107333
|
]
|
|
107504
107334
|
}));
|
|
@@ -108035,8 +107865,8 @@ init_exitCode();
|
|
|
108035
107865
|
init_maintenanceStorage();
|
|
108036
107866
|
init_productionFeedback();
|
|
108037
107867
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
108038
|
-
import { readFile as
|
|
108039
|
-
import { join as
|
|
107868
|
+
import { readFile as readFile76 } from "node:fs/promises";
|
|
107869
|
+
import { join as join96 } from "node:path";
|
|
108040
107870
|
async function beginProductionRevision(input) {
|
|
108041
107871
|
return withProductionFeedback({ operation: "revision" }, () => withProjectWriteLock(input.projectRoot, "production-revision", async () => {
|
|
108042
107872
|
await recoverDurableMultiFileTransactions(input.projectRoot);
|
|
@@ -108075,7 +107905,7 @@ async function beginProductionRevision(input) {
|
|
|
108075
107905
|
const formal = approved.byPath.get(path3);
|
|
108076
107906
|
if (!prior && !formal)
|
|
108077
107907
|
throw invalid2("Write the current task first; there is no article draft to revise yet.");
|
|
108078
|
-
const markdown = prior?.body ?? await
|
|
107908
|
+
const markdown = prior?.body ?? await readFile76(await safeProjectTarget(input.projectRoot, join96("knowledge", path3)), "utf8");
|
|
108079
107909
|
const sections = prior?.indexer_candidate.sections.map((section) => ({ id: section.section_key, references: section.references })) ?? formal?.sections;
|
|
108080
107910
|
const sources = [];
|
|
108081
107911
|
for (const source2 of owner.sources) {
|
|
@@ -108251,8 +108081,8 @@ init_actionInputWorkspace();
|
|
|
108251
108081
|
init_cliFeedback();
|
|
108252
108082
|
init_errors3();
|
|
108253
108083
|
init_exitCode();
|
|
108254
|
-
var
|
|
108255
|
-
import { readFile as
|
|
108084
|
+
var import_yaml41 = __toESM(require_dist(), 1);
|
|
108085
|
+
import { readFile as readFile77 } from "node:fs/promises";
|
|
108256
108086
|
function userInputError2(message, detail = {}) {
|
|
108257
108087
|
return new ContextError(ExitCode.UserError, message, {
|
|
108258
108088
|
category: ErrorCategory.UserInputInvalid,
|
|
@@ -108263,7 +108093,7 @@ function parsePayloadText(raw) {
|
|
|
108263
108093
|
const trimmed = raw.trimStart();
|
|
108264
108094
|
if (trimmed.startsWith("{") || trimmed.startsWith("["))
|
|
108265
108095
|
return JSON.parse(raw);
|
|
108266
|
-
return
|
|
108096
|
+
return import_yaml41.default.parse(raw);
|
|
108267
108097
|
}
|
|
108268
108098
|
function isCompleteJsonLine(raw) {
|
|
108269
108099
|
if (!raw.endsWith(`
|
|
@@ -108321,7 +108151,7 @@ async function readPayloadTextFromStdin(stdin) {
|
|
|
108321
108151
|
}
|
|
108322
108152
|
async function readPayloadText(path3) {
|
|
108323
108153
|
if (path3 !== "-")
|
|
108324
|
-
return
|
|
108154
|
+
return readFile77(path3, "utf8");
|
|
108325
108155
|
return readPayloadTextFromStdin(process.stdin);
|
|
108326
108156
|
}
|
|
108327
108157
|
async function readYamlOrJsonInput(input) {
|
|
@@ -108683,7 +108513,7 @@ function registerRuntimeEventLogCommands(program2) {
|
|
|
108683
108513
|
if (result.status === "pending") {
|
|
108684
108514
|
const reason = result.last_result?.reason;
|
|
108685
108515
|
const requiresNetworkAccess = isNetworkFailure(reason);
|
|
108686
|
-
throw new ContextError(ExitCode.ExternalToolError, requiresNetworkAccess ? "runtime event delivery could not reach the configured sink" : "runtime event delivery was rejected by the configured sink", {
|
|
108516
|
+
throw new ContextError(ExitCode.ExternalToolError, requiresNetworkAccess ? "runtime event delivery could not reach the configured sink" : reason === "invalid_batch" ? "local telemetry bridge rejected the batch before network delivery; update the sink CLI for protocol compatibility" : "runtime event delivery was rejected by the configured sink", {
|
|
108687
108517
|
category: ErrorCategory.ExternalToolFailed,
|
|
108688
108518
|
reason_code: requiresNetworkAccess ? "runtime-events-network-unavailable" : "runtime-events-delivery-failed",
|
|
108689
108519
|
pending_count: result.pending_count,
|
|
@@ -108712,10 +108542,10 @@ init_actionInputWorkspace();
|
|
|
108712
108542
|
|
|
108713
108543
|
// src/project/actionCompletionOutput.ts
|
|
108714
108544
|
init_atomicWrite();
|
|
108715
|
-
var
|
|
108545
|
+
var import_yaml44 = __toESM(require_dist(), 1);
|
|
108716
108546
|
import { Buffer as Buffer4 } from "node:buffer";
|
|
108717
|
-
import { createHash as
|
|
108718
|
-
import { join as
|
|
108547
|
+
import { createHash as createHash30 } from "node:crypto";
|
|
108548
|
+
import { join as join100 } from "node:path";
|
|
108719
108549
|
var INLINE_LIMIT = 16 * 1024;
|
|
108720
108550
|
function record4(value) {
|
|
108721
108551
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
@@ -108728,7 +108558,7 @@ function shortText(value, limit = 400) {
|
|
|
108728
108558
|
}
|
|
108729
108559
|
function serializeActionCompletion(value, format2) {
|
|
108730
108560
|
return format2 === "json" ? `${JSON.stringify(value, null, 2)}
|
|
108731
|
-
` :
|
|
108561
|
+
` : import_yaml44.default.stringify(value);
|
|
108732
108562
|
}
|
|
108733
108563
|
async function prepareActionCompletionOutput(input) {
|
|
108734
108564
|
if (input.verbose)
|
|
@@ -108740,9 +108570,9 @@ async function prepareActionCompletionOutput(input) {
|
|
|
108740
108570
|
const production = typeof result.stage_state === "string";
|
|
108741
108571
|
if (production && Buffer4.byteLength(full) <= INLINE_LIMIT)
|
|
108742
108572
|
return input.result;
|
|
108743
|
-
const digest6 =
|
|
108744
|
-
const root2 =
|
|
108745
|
-
const resultFile =
|
|
108573
|
+
const digest6 = createHash30("sha256").update(full).digest("hex");
|
|
108574
|
+
const root2 = join100(input.projectRoot, ".tmp/context-runtime/action-results");
|
|
108575
|
+
const resultFile = join100(root2, `${digest6}.json`);
|
|
108746
108576
|
await atomicWriteFile(resultFile, full);
|
|
108747
108577
|
if (production)
|
|
108748
108578
|
return {
|
|
@@ -108753,7 +108583,7 @@ async function prepareActionCompletionOutput(input) {
|
|
|
108753
108583
|
...pick(result, ["next", "next_preparation"])
|
|
108754
108584
|
};
|
|
108755
108585
|
const next2 = record4(result.next) ?? record4(record4(result.workflow)?.current) ?? record4(record4(result.continuation)?.next);
|
|
108756
|
-
const nextFile = next2 === undefined ? undefined :
|
|
108586
|
+
const nextFile = next2 === undefined ? undefined : join100(root2, `${digest6}.next.json`);
|
|
108757
108587
|
if (nextFile !== undefined)
|
|
108758
108588
|
await atomicWriteFile(nextFile, serializeActionCompletion(next2, "json"));
|
|
108759
108589
|
const outcomes = (Array.isArray(result.outcomes) ? result.outcomes : []).map(record4).filter((item) => item !== undefined);
|
|
@@ -108794,7 +108624,7 @@ async function prepareActionCompletionOutput(input) {
|
|
|
108794
108624
|
]),
|
|
108795
108625
|
next_route: next2 === undefined ? null : {
|
|
108796
108626
|
file: nextFile,
|
|
108797
|
-
digest: `sha256:${
|
|
108627
|
+
digest: `sha256:${createHash30("sha256").update(serializeActionCompletion(next2, "json")).digest("hex")}`,
|
|
108798
108628
|
...pick(next2, ["revision", "node", "availability"]),
|
|
108799
108629
|
commands: next2.commands,
|
|
108800
108630
|
gate: next2.gate === undefined ? undefined : pick(record4(next2.gate), ["id", "resolution", "delegatable"])
|
|
@@ -108841,7 +108671,7 @@ init_errors3();
|
|
|
108841
108671
|
init_cliFeedback();
|
|
108842
108672
|
init_exitCode();
|
|
108843
108673
|
init_productionFeedback();
|
|
108844
|
-
var
|
|
108674
|
+
var import_yaml45 = __toESM(require_dist(), 1);
|
|
108845
108675
|
import { relative as relative24, resolve as resolve32 } from "node:path";
|
|
108846
108676
|
async function completeCurrentProductionAction(input) {
|
|
108847
108677
|
return withProductionFeedback({ operation: "action", file: input.submissionPath }, () => withProjectWriteLock(input.projectRoot, "production-action", async () => {
|
|
@@ -108860,7 +108690,7 @@ async function completeCurrentProductionAction(input) {
|
|
|
108860
108690
|
throw invalid3("Production requires a stage-local manifest file, not stdin.");
|
|
108861
108691
|
const path3 = relative24(resolve32(input.projectRoot, productionAgentDirectory(stage.id)), resolve32(input.cwd, input.submissionPath));
|
|
108862
108692
|
const manifest = await readProductionFile({ projectRoot: input.projectRoot, stage: stage.id, path: path3 });
|
|
108863
|
-
const value =
|
|
108693
|
+
const value = import_yaml45.default.parse(manifest.text, { uniqueKeys: true });
|
|
108864
108694
|
if (!value || typeof value !== "object" || !("stage" in value) || value.stage !== stage.id) {
|
|
108865
108695
|
throw invalid3("The manifest must identify the current production stage. No tasks were saved.");
|
|
108866
108696
|
}
|
|
@@ -109007,7 +108837,7 @@ init_errors3();
|
|
|
109007
108837
|
init_cliFeedback();
|
|
109008
108838
|
init_exitCode();
|
|
109009
108839
|
init_productionFeedback();
|
|
109010
|
-
var
|
|
108840
|
+
var import_yaml46 = __toESM(require_dist(), 1);
|
|
109011
108841
|
import { relative as relative25, resolve as resolve33 } from "node:path";
|
|
109012
108842
|
var productionKnownTasksSchema = productionPlanInputSchema.omit({ stage: true });
|
|
109013
108843
|
async function prepareKnownProductionTasks(input) {
|
|
@@ -109022,7 +108852,7 @@ async function prepareKnownProductionTasks(input) {
|
|
|
109022
108852
|
input_schema: zodToJsonSchema(productionKnownTasksSchema, { $refStrategy: "none" }),
|
|
109023
108853
|
next_action: { command: "context status --format json" }
|
|
109024
108854
|
});
|
|
109025
|
-
const plan = productionKnownTasksSchema.parse(
|
|
108855
|
+
const plan = productionKnownTasksSchema.parse(import_yaml46.default.parse(fixed2.text, { uniqueKeys: true }));
|
|
109026
108856
|
const request = await productionPlanningRequest(input.projectRoot);
|
|
109027
108857
|
let stage = await readProductionStage(input.projectRoot);
|
|
109028
108858
|
if (!request || stage?.report_approved || (stage ? ![stage.id, request.revision].includes(input.revision) : request.revision !== input.revision)) {
|
|
@@ -109032,7 +108862,7 @@ async function prepareKnownProductionTasks(input) {
|
|
|
109032
108862
|
await prepareCurrentProductionStage({ projectRoot: input.projectRoot, revision: input.revision });
|
|
109033
108863
|
stage = await readProductionStage(input.projectRoot);
|
|
109034
108864
|
}
|
|
109035
|
-
const text10 =
|
|
108865
|
+
const text10 = import_yaml46.default.stringify({ ...plan, stage: stage.id });
|
|
109036
108866
|
const result = await submitProductionPlan({
|
|
109037
108867
|
projectRoot: input.projectRoot,
|
|
109038
108868
|
stage: stage.id,
|
|
@@ -109154,9 +108984,9 @@ function registerProjectActionCommands(program2) {
|
|
|
109154
108984
|
// src/commands/cleanClaudePluginCache.ts
|
|
109155
108985
|
init_cliFeedback();
|
|
109156
108986
|
import { existsSync as existsSync27 } from "node:fs";
|
|
109157
|
-
import { readdir as
|
|
108987
|
+
import { readdir as readdir26, rm as rm20 } from "node:fs/promises";
|
|
109158
108988
|
import { homedir } from "node:os";
|
|
109159
|
-
import { join as
|
|
108989
|
+
import { join as join101 } from "node:path";
|
|
109160
108990
|
var ORPHAN_MARKER = ".orphaned_at";
|
|
109161
108991
|
var CLAUDE_PLUGIN_CACHE_ROOT_ENV = "C4A_CLAUDE_PLUGIN_CACHE_ROOT";
|
|
109162
108992
|
var CLAUDE_PLUGIN_CACHE_HOME_ENV = "C4A_CLAUDE_PLUGIN_CACHE_HOME";
|
|
@@ -109169,23 +108999,23 @@ async function cleanClaudePluginCache(opts = {}) {
|
|
|
109169
108999
|
lines.push("· claude plugin cache: missing — nothing to clean");
|
|
109170
109000
|
return { lines, removed, scanned };
|
|
109171
109001
|
}
|
|
109172
|
-
const marketplaces = await
|
|
109002
|
+
const marketplaces = await readdir26(cacheRoot, { withFileTypes: true });
|
|
109173
109003
|
for (const mp of marketplaces) {
|
|
109174
109004
|
if (!mp.isDirectory())
|
|
109175
109005
|
continue;
|
|
109176
|
-
const mpDir =
|
|
109177
|
-
const plugins = await
|
|
109006
|
+
const mpDir = join101(cacheRoot, mp.name);
|
|
109007
|
+
const plugins = await readdir26(mpDir, { withFileTypes: true });
|
|
109178
109008
|
for (const pl of plugins) {
|
|
109179
109009
|
if (!pl.isDirectory())
|
|
109180
109010
|
continue;
|
|
109181
|
-
const plDir =
|
|
109182
|
-
const versions = await
|
|
109011
|
+
const plDir = join101(mpDir, pl.name);
|
|
109012
|
+
const versions = await readdir26(plDir, { withFileTypes: true });
|
|
109183
109013
|
for (const ver of versions) {
|
|
109184
109014
|
if (!ver.isDirectory())
|
|
109185
109015
|
continue;
|
|
109186
109016
|
scanned += 1;
|
|
109187
|
-
const verDir =
|
|
109188
|
-
const markerPath =
|
|
109017
|
+
const verDir = join101(plDir, ver.name);
|
|
109018
|
+
const markerPath = join101(verDir, ORPHAN_MARKER);
|
|
109189
109019
|
if (!existsSync27(markerPath))
|
|
109190
109020
|
continue;
|
|
109191
109021
|
const label2 = `${mp.name}/${pl.name}/${ver.name}`;
|
|
@@ -109217,11 +109047,11 @@ function resolveClaudePluginCacheRoot(opts) {
|
|
|
109217
109047
|
if (explicitRoot)
|
|
109218
109048
|
return explicitRoot;
|
|
109219
109049
|
const home = opts.home ?? process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV] ?? homedir();
|
|
109220
|
-
return
|
|
109050
|
+
return join101(home, ".claude", "plugins", "cache");
|
|
109221
109051
|
}
|
|
109222
109052
|
async function isEmptyDir(dir) {
|
|
109223
109053
|
try {
|
|
109224
|
-
const entries2 = await
|
|
109054
|
+
const entries2 = await readdir26(dir);
|
|
109225
109055
|
return entries2.length === 0;
|
|
109226
109056
|
} catch {
|
|
109227
109057
|
return false;
|
|
@@ -109249,13 +109079,13 @@ init_exitCode();
|
|
|
109249
109079
|
|
|
109250
109080
|
// src/lib/packageVersion.ts
|
|
109251
109081
|
import { existsSync as existsSync28, readFileSync as readFileSync9 } from "node:fs";
|
|
109252
|
-
import { dirname as
|
|
109082
|
+
import { dirname as dirname41, join as join102 } from "node:path";
|
|
109253
109083
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
109254
109084
|
function readPackageVersion() {
|
|
109255
109085
|
try {
|
|
109256
|
-
let dir =
|
|
109086
|
+
let dir = dirname41(fileURLToPath7(import.meta.url));
|
|
109257
109087
|
for (let depth = 0;depth < 8; depth += 1) {
|
|
109258
|
-
const packagePath =
|
|
109088
|
+
const packagePath = join102(dir, "package.json");
|
|
109259
109089
|
if (existsSync28(packagePath)) {
|
|
109260
109090
|
const parsed = JSON.parse(readFileSync9(packagePath, "utf8"));
|
|
109261
109091
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
@@ -109263,7 +109093,7 @@ function readPackageVersion() {
|
|
|
109263
109093
|
}
|
|
109264
109094
|
break;
|
|
109265
109095
|
}
|
|
109266
|
-
const parent =
|
|
109096
|
+
const parent = dirname41(dir);
|
|
109267
109097
|
if (parent === dir)
|
|
109268
109098
|
break;
|
|
109269
109099
|
dir = parent;
|
|
@@ -109278,24 +109108,24 @@ function readPackageVersion() {
|
|
|
109278
109108
|
|
|
109279
109109
|
// src/project/sourceCommands.ts
|
|
109280
109110
|
init_src2();
|
|
109281
|
-
import { readFile as
|
|
109111
|
+
import { readFile as readFile90 } from "node:fs/promises";
|
|
109282
109112
|
import { isAbsolute as isAbsolute22, resolve as resolve37 } from "node:path";
|
|
109283
109113
|
init_cliFeedback();
|
|
109284
109114
|
init_errors3();
|
|
109285
109115
|
init_exitCode();
|
|
109286
|
-
var
|
|
109116
|
+
var import_yaml50 = __toESM(require_dist(), 1);
|
|
109287
109117
|
|
|
109288
109118
|
// src/project/repoSourceRecovery.ts
|
|
109289
109119
|
init_cliFeedback();
|
|
109290
109120
|
init_errors3();
|
|
109291
109121
|
init_exitCode();
|
|
109292
|
-
import { execFile as
|
|
109122
|
+
import { execFile as execFile12 } from "node:child_process";
|
|
109293
109123
|
import { existsSync as existsSync29 } from "node:fs";
|
|
109294
109124
|
import { lstat as lstat12, mkdir as mkdir34, readlink as readlink2, realpath as realpath10, rm as rm21, symlink as symlink4 } from "node:fs/promises";
|
|
109295
|
-
import { basename as basename11, dirname as
|
|
109296
|
-
import { promisify as
|
|
109125
|
+
import { basename as basename11, dirname as dirname42, isAbsolute as isAbsolute19, relative as relative26, resolve as resolve34 } from "node:path";
|
|
109126
|
+
import { promisify as promisify12 } from "node:util";
|
|
109297
109127
|
init_writeLock();
|
|
109298
|
-
var execFileAsync6 =
|
|
109128
|
+
var execFileAsync6 = promisify12(execFile12);
|
|
109299
109129
|
var RECOVERY_SCHEMA = "context.repository-source-recovery.v1";
|
|
109300
109130
|
function userInputError3(message, detail = {}) {
|
|
109301
109131
|
return new ContextError(ExitCode.UserError, message, {
|
|
@@ -109477,7 +109307,7 @@ async function cloneCheckout(input) {
|
|
|
109477
109307
|
next: `Use local mode with path ${JSON.stringify(target)} after inspecting the existing checkout.`
|
|
109478
109308
|
});
|
|
109479
109309
|
}
|
|
109480
|
-
await mkdir34(
|
|
109310
|
+
await mkdir34(dirname42(target), { recursive: true });
|
|
109481
109311
|
const cloneArgs = ["clone", "--no-checkout", "--depth=1", "--filter=blob:none", input.remote, target];
|
|
109482
109312
|
try {
|
|
109483
109313
|
await execFileAsync6("git", cloneArgs, { encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
|
|
@@ -109526,7 +109356,7 @@ async function bindLocalAlias(input) {
|
|
|
109526
109356
|
const stats = await lstat12(alias).catch(() => null);
|
|
109527
109357
|
if (stats !== null) {
|
|
109528
109358
|
if (stats.isSymbolicLink()) {
|
|
109529
|
-
const actual = resolve34(
|
|
109359
|
+
const actual = resolve34(dirname42(alias), await readlink2(alias));
|
|
109530
109360
|
const actualReal = await realpath10(actual).catch(() => null);
|
|
109531
109361
|
if (actualReal !== null && actualReal === await realpath10(input.checkout))
|
|
109532
109362
|
return;
|
|
@@ -109542,8 +109372,8 @@ async function bindLocalAlias(input) {
|
|
|
109542
109372
|
});
|
|
109543
109373
|
}
|
|
109544
109374
|
}
|
|
109545
|
-
await mkdir34(
|
|
109546
|
-
await symlink4(relative26(
|
|
109375
|
+
await mkdir34(dirname42(alias), { recursive: true });
|
|
109376
|
+
await symlink4(relative26(dirname42(alias), input.checkout) || ".", alias);
|
|
109547
109377
|
}
|
|
109548
109378
|
function selectPhysicalGroup(sources, selector) {
|
|
109549
109379
|
const direct = selectRepoSources(sources, selector);
|
|
@@ -109637,11 +109467,11 @@ async function restoreRepositorySources(input) {
|
|
|
109637
109467
|
// src/project/sourceDocumentStatus.ts
|
|
109638
109468
|
init_src2();
|
|
109639
109469
|
import { existsSync as existsSync30 } from "node:fs";
|
|
109640
|
-
import { readFile as
|
|
109641
|
-
import { join as
|
|
109470
|
+
import { readFile as readFile83 } from "node:fs/promises";
|
|
109471
|
+
import { join as join104 } from "node:path";
|
|
109642
109472
|
// src/project/sourceCommandViews.ts
|
|
109643
|
-
import { readFile as
|
|
109644
|
-
import { join as
|
|
109473
|
+
import { readFile as readFile82 } from "node:fs/promises";
|
|
109474
|
+
import { join as join103 } from "node:path";
|
|
109645
109475
|
init_workspace();
|
|
109646
109476
|
init_documentBatchManifest();
|
|
109647
109477
|
function repoSourceAgentView(source2) {
|
|
@@ -109704,7 +109534,7 @@ async function fileSourceAgentViewWithNextAction(input) {
|
|
|
109704
109534
|
};
|
|
109705
109535
|
}
|
|
109706
109536
|
function documentSourceManifestPath(source2) {
|
|
109707
|
-
return source2.snapshot?.manifest ??
|
|
109537
|
+
return source2.snapshot?.manifest ?? join103(source2.materializedAt, "manifest.json");
|
|
109708
109538
|
}
|
|
109709
109539
|
async function fileSourceDocumentSiteHint(input) {
|
|
109710
109540
|
const detection = await detectDocumentSiteFiles({
|
|
@@ -109714,7 +109544,7 @@ async function fileSourceDocumentSiteHint(input) {
|
|
|
109714
109544
|
let snapshotConfigured = false;
|
|
109715
109545
|
const manifest = documentSourceManifestPath(input.source);
|
|
109716
109546
|
try {
|
|
109717
|
-
const parsed = parseDocumentSnapshotForSource(JSON.parse(await
|
|
109547
|
+
const parsed = parseDocumentSnapshotForSource(JSON.parse(await readFile82(join103(input.projectRoot, manifest), "utf8")), input.source.name);
|
|
109718
109548
|
snapshotConfigured = manifestUsesMdxJsonDocs(parsed);
|
|
109719
109549
|
} catch {
|
|
109720
109550
|
snapshotConfigured = false;
|
|
@@ -109746,11 +109576,11 @@ async function larkSourceAgentViewWithNextAction(input) {
|
|
|
109746
109576
|
// src/project/sourceDocumentStatus.ts
|
|
109747
109577
|
init_documentBatchManifest();
|
|
109748
109578
|
function documentSourceManifestPath2(source2) {
|
|
109749
|
-
return source2.snapshot?.manifest ??
|
|
109579
|
+
return source2.snapshot?.manifest ?? join104(source2.materializedAt, "manifest.json");
|
|
109750
109580
|
}
|
|
109751
109581
|
async function documentSnapshotState(input) {
|
|
109752
109582
|
const manifest = documentSourceManifestPath2(input.source);
|
|
109753
|
-
const manifestPath =
|
|
109583
|
+
const manifestPath = join104(input.projectRoot, manifest);
|
|
109754
109584
|
if (!existsSync30(manifestPath)) {
|
|
109755
109585
|
return {
|
|
109756
109586
|
snapshotReady: false,
|
|
@@ -109761,7 +109591,7 @@ async function documentSnapshotState(input) {
|
|
|
109761
109591
|
};
|
|
109762
109592
|
}
|
|
109763
109593
|
try {
|
|
109764
|
-
const parsed = findDocumentSnapshotForSource(JSON.parse(await
|
|
109594
|
+
const parsed = findDocumentSnapshotForSource(JSON.parse(await readFile83(manifestPath, "utf8")), input.source.name);
|
|
109765
109595
|
if (parsed === null) {
|
|
109766
109596
|
return {
|
|
109767
109597
|
snapshotReady: false,
|
|
@@ -109829,7 +109659,7 @@ async function documentSnapshotState(input) {
|
|
|
109829
109659
|
const missing = [
|
|
109830
109660
|
...parsed.files.map((file) => file.path),
|
|
109831
109661
|
...(parsed.assets ?? []).filter((asset) => asset.content_hash !== undefined).map((asset) => asset.path)
|
|
109832
|
-
].find((path3) => !existsSync30(
|
|
109662
|
+
].find((path3) => !existsSync30(join104(input.projectRoot, input.source.materializedAt, path3)));
|
|
109833
109663
|
if (missing !== undefined) {
|
|
109834
109664
|
return {
|
|
109835
109665
|
snapshotReady: false,
|
|
@@ -109928,10 +109758,10 @@ init_atomicWrite();
|
|
|
109928
109758
|
init_cliFeedback();
|
|
109929
109759
|
init_errors3();
|
|
109930
109760
|
init_exitCode();
|
|
109931
|
-
var
|
|
109932
|
-
import { createHash as
|
|
109933
|
-
import { readFile as
|
|
109934
|
-
import { basename as basename12, extname as extname15, isAbsolute as isAbsolute20, join as
|
|
109761
|
+
var import_yaml47 = __toESM(require_dist(), 1);
|
|
109762
|
+
import { createHash as createHash31 } from "node:crypto";
|
|
109763
|
+
import { readFile as readFile84, realpath as realpath11 } from "node:fs/promises";
|
|
109764
|
+
import { basename as basename12, extname as extname15, isAbsolute as isAbsolute20, join as join105, relative as relative27, resolve as resolve35 } from "node:path";
|
|
109935
109765
|
init_writeLock();
|
|
109936
109766
|
var SOURCE_NAME_PATTERN3 = /^[a-z0-9][a-z0-9._-]*$/u;
|
|
109937
109767
|
function isDateSourceNamespace(value) {
|
|
@@ -109960,7 +109790,7 @@ function defaultLarkModule(input) {
|
|
|
109960
109790
|
if (titleSlug.length > 0)
|
|
109961
109791
|
return titleSlug;
|
|
109962
109792
|
}
|
|
109963
|
-
const opaqueSlug = (kind, identity) => `${kind}-${
|
|
109793
|
+
const opaqueSlug = (kind, identity) => `${kind}-${createHash31("sha256").update(identity).digest("hex").slice(0, 12)}`;
|
|
109964
109794
|
if (input.url !== undefined) {
|
|
109965
109795
|
try {
|
|
109966
109796
|
const parsed = new URL(input.url);
|
|
@@ -110027,8 +109857,8 @@ function assertSafeFileInclude(value) {
|
|
|
110027
109857
|
}
|
|
110028
109858
|
async function readRegistryDocument(projectRoot, registryPath2) {
|
|
110029
109859
|
try {
|
|
110030
|
-
const content3 = await
|
|
110031
|
-
return content3.trim().length === 0 ? { sources: [] } :
|
|
109860
|
+
const content3 = await readFile84(join105(projectRoot, registryPath2), "utf8");
|
|
109861
|
+
return content3.trim().length === 0 ? { sources: [] } : import_yaml47.default.parse(content3);
|
|
110032
109862
|
} catch (error) {
|
|
110033
109863
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
110034
109864
|
return { sources: [] };
|
|
@@ -110145,7 +109975,7 @@ async function addFileSourceUnlocked(input) {
|
|
|
110145
109975
|
const record6 = entry2;
|
|
110146
109976
|
return record6.name !== input.name && record6.id !== input.name;
|
|
110147
109977
|
}), nextEntry];
|
|
110148
|
-
await atomicWriteFile(
|
|
109978
|
+
await atomicWriteFile(join105(input.projectRoot, DEFAULT_FILE_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
|
|
110149
109979
|
const updated = await loadSourcesRegistry({ rootDir: input.projectRoot });
|
|
110150
109980
|
const entry = updated.files.find((source2) => source2.name === input.name || source2.id === input.name);
|
|
110151
109981
|
if (entry === undefined) {
|
|
@@ -110201,7 +110031,7 @@ async function addLarkSourceUnlocked(input) {
|
|
|
110201
110031
|
const record6 = entry2;
|
|
110202
110032
|
return record6.name !== input.name && record6.id !== input.name;
|
|
110203
110033
|
}), nextEntry];
|
|
110204
|
-
await atomicWriteFile(
|
|
110034
|
+
await atomicWriteFile(join105(input.projectRoot, DEFAULT_LARK_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
|
|
110205
110035
|
const updated = await loadSourcesRegistry({ rootDir: input.projectRoot });
|
|
110206
110036
|
const entry = updated.larks.find((source2) => source2.name === input.name || source2.id === input.name);
|
|
110207
110037
|
if (entry === undefined) {
|
|
@@ -110307,12 +110137,12 @@ function parseBatchItem(value, index2) {
|
|
|
110307
110137
|
const url = optionalString(record6, "url", path3);
|
|
110308
110138
|
const docToken = optionalString(record6, "docToken", path3);
|
|
110309
110139
|
const wikiToken = optionalString(record6, "wikiToken", path3);
|
|
110310
|
-
const
|
|
110140
|
+
const title2 = optionalString(record6, "title", path3);
|
|
110311
110141
|
const module = optionalString(record6, "module", path3) ?? defaultLarkModule({
|
|
110312
110142
|
...url !== undefined ? { url } : {},
|
|
110313
110143
|
...docToken !== undefined ? { docToken } : {},
|
|
110314
110144
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
110315
|
-
...
|
|
110145
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
110316
110146
|
});
|
|
110317
110147
|
return {
|
|
110318
110148
|
type,
|
|
@@ -110320,7 +110150,7 @@ function parseBatchItem(value, index2) {
|
|
|
110320
110150
|
...url !== undefined ? { url } : {},
|
|
110321
110151
|
...docToken !== undefined ? { docToken } : {},
|
|
110322
110152
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
110323
|
-
...
|
|
110153
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
110324
110154
|
};
|
|
110325
110155
|
}
|
|
110326
110156
|
function parseBatchPayload(payload) {
|
|
@@ -110401,11 +110231,11 @@ init_candidateLedger();
|
|
|
110401
110231
|
init_documentBatchManifest();
|
|
110402
110232
|
init_workspace();
|
|
110403
110233
|
init_writeLock();
|
|
110404
|
-
var
|
|
110234
|
+
var import_yaml48 = __toESM(require_dist(), 1);
|
|
110405
110235
|
import { existsSync as existsSync31 } from "node:fs";
|
|
110406
|
-
import { createHash as
|
|
110407
|
-
import { readFile as
|
|
110408
|
-
import { isAbsolute as isAbsolute21, join as
|
|
110236
|
+
import { createHash as createHash32 } from "node:crypto";
|
|
110237
|
+
import { readFile as readFile85, readdir as readdir27, rm as rm22 } from "node:fs/promises";
|
|
110238
|
+
import { isAbsolute as isAbsolute21, join as join106, relative as relative28, resolve as resolve36, sep as sep8 } from "node:path";
|
|
110409
110239
|
function sourceIdentity(source2) {
|
|
110410
110240
|
if (source2.kind === "source.collection")
|
|
110411
110241
|
return;
|
|
@@ -110437,10 +110267,10 @@ function collectStrings(value, output) {
|
|
|
110437
110267
|
}
|
|
110438
110268
|
}
|
|
110439
110269
|
async function yamlReferences(input) {
|
|
110440
|
-
const absolutePath =
|
|
110270
|
+
const absolutePath = join106(input.projectRoot, input.path);
|
|
110441
110271
|
if (!existsSync31(absolutePath))
|
|
110442
110272
|
return false;
|
|
110443
|
-
const parsed =
|
|
110273
|
+
const parsed = import_yaml48.default.parse(await readFile85(absolutePath, "utf8"));
|
|
110444
110274
|
const strings = [];
|
|
110445
110275
|
collectStrings(parsed, strings);
|
|
110446
110276
|
return strings.some((value) => stringReferencesSource(value, input.source));
|
|
@@ -110566,11 +110396,11 @@ async function registryRemovalWrite(projectRoot, source2) {
|
|
|
110566
110396
|
const path3 = registryPath2(source2.type);
|
|
110567
110397
|
if (path3 === null)
|
|
110568
110398
|
return;
|
|
110569
|
-
const absolutePath =
|
|
110570
|
-
const document4 = existsSync31(absolutePath) ?
|
|
110399
|
+
const absolutePath = join106(projectRoot, path3);
|
|
110400
|
+
const document4 = existsSync31(absolutePath) ? import_yaml48.default.parse(await readFile85(absolutePath, "utf8")) : { sources: [] };
|
|
110571
110401
|
return {
|
|
110572
110402
|
path: absolutePath,
|
|
110573
|
-
bytes:
|
|
110403
|
+
bytes: import_yaml48.default.stringify(removeDocumentEntry(document4, source2))
|
|
110574
110404
|
};
|
|
110575
110405
|
}
|
|
110576
110406
|
function safeManagedMaterializedPath(projectRoot, source2) {
|
|
@@ -110585,7 +110415,7 @@ function safeManagedMaterializedPath(projectRoot, source2) {
|
|
|
110585
110415
|
return absolute;
|
|
110586
110416
|
}
|
|
110587
110417
|
function safeManagedManifestPath(projectRoot, source2) {
|
|
110588
|
-
const manifest = source2.manifest ??
|
|
110418
|
+
const manifest = source2.manifest ?? join106(source2.materializedAt, "manifest.json");
|
|
110589
110419
|
if (isAbsolute21(manifest))
|
|
110590
110420
|
throw unsafeOwnership(source2, manifest);
|
|
110591
110421
|
const absolute = resolve36(projectRoot, manifest);
|
|
@@ -110620,7 +110450,7 @@ function projectRelative(projectRoot, path3) {
|
|
|
110620
110450
|
return relative28(projectRoot, path3).split(sep8).join("/");
|
|
110621
110451
|
}
|
|
110622
110452
|
function digest6(value) {
|
|
110623
|
-
return `sha256:${
|
|
110453
|
+
return `sha256:${createHash32("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
110624
110454
|
}
|
|
110625
110455
|
async function sharedMaterializedOwners(projectRoot, source2) {
|
|
110626
110456
|
const target = safeManagedMaterializedPath(projectRoot, source2);
|
|
@@ -110714,7 +110544,7 @@ async function createRemovalPlan(projectRoot, selector) {
|
|
|
110714
110544
|
source: source2,
|
|
110715
110545
|
registry: registryPath2(source2.type),
|
|
110716
110546
|
registryBytes: registryWrite?.bytes ?? null,
|
|
110717
|
-
managedBytes: source2.type === "note" || source2.type === "sessions" ? await
|
|
110547
|
+
managedBytes: source2.type === "note" || source2.type === "sessions" ? await readFile85(absoluteRemovals[0], "utf8") : null,
|
|
110718
110548
|
references,
|
|
110719
110549
|
cleanup,
|
|
110720
110550
|
manifestBytes: manifestWrite?.bytes ?? null
|
|
@@ -110747,10 +110577,10 @@ function publicRemovalResult(plan, action) {
|
|
|
110747
110577
|
};
|
|
110748
110578
|
}
|
|
110749
110579
|
async function pruneExtractRuntime(projectRoot, source2) {
|
|
110750
|
-
const fingerprintPath =
|
|
110580
|
+
const fingerprintPath = join106(projectRoot, ".tmp/context-runtime/extract/source-fingerprints.json");
|
|
110751
110581
|
const removedPhaseIds = new Set;
|
|
110752
110582
|
if (existsSync31(fingerprintPath)) {
|
|
110753
|
-
const parsed = JSON.parse(await
|
|
110583
|
+
const parsed = JSON.parse(await readFile85(fingerprintPath, "utf8"));
|
|
110754
110584
|
const phases = parsed.phases ?? {};
|
|
110755
110585
|
const next2 = Object.fromEntries(Object.entries(phases).filter(([phaseId, raw]) => {
|
|
110756
110586
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
@@ -110764,27 +110594,27 @@ async function pruneExtractRuntime(projectRoot, source2) {
|
|
|
110764
110594
|
await atomicWriteFile(fingerprintPath, `${JSON.stringify({ ...parsed, phases: next2 }, null, 2)}
|
|
110765
110595
|
`);
|
|
110766
110596
|
}
|
|
110767
|
-
const phaseOwnershipPath =
|
|
110597
|
+
const phaseOwnershipPath = join106(projectRoot, ".tmp/context-runtime/extract/custom-phase-candidates.json");
|
|
110768
110598
|
if (existsSync31(phaseOwnershipPath) && removedPhaseIds.size > 0) {
|
|
110769
|
-
const parsed = JSON.parse(await
|
|
110599
|
+
const parsed = JSON.parse(await readFile85(phaseOwnershipPath, "utf8"));
|
|
110770
110600
|
const phases = Object.fromEntries(Object.entries(parsed.phases ?? {}).filter(([phaseId]) => !removedPhaseIds.has(phaseId)));
|
|
110771
110601
|
await atomicWriteFile(phaseOwnershipPath, `${JSON.stringify({ ...parsed, phases }, null, 2)}
|
|
110772
110602
|
`);
|
|
110773
110603
|
}
|
|
110774
|
-
const symbolPath =
|
|
110604
|
+
const symbolPath = join106(projectRoot, ".tmp/context-runtime/extract/source-symbols.json");
|
|
110775
110605
|
if (existsSync31(symbolPath)) {
|
|
110776
|
-
const parsed = JSON.parse(await
|
|
110606
|
+
const parsed = JSON.parse(await readFile85(symbolPath, "utf8"));
|
|
110777
110607
|
const symbols = Array.isArray(parsed.symbols) ? parsed.symbols.filter((entry) => entry === null || typeof entry !== "object" || Array.isArray(entry) || entry.source !== source2.name) : [];
|
|
110778
110608
|
const phaseFingerprints = Object.fromEntries(Object.entries(parsed.phaseFingerprints ?? {}).filter(([phaseId]) => !removedPhaseIds.has(phaseId)));
|
|
110779
110609
|
await atomicWriteFile(symbolPath, `${JSON.stringify({ ...parsed, phaseFingerprints, symbols }, null, 2)}
|
|
110780
110610
|
`);
|
|
110781
110611
|
}
|
|
110782
|
-
const snapshotRoot =
|
|
110612
|
+
const snapshotRoot = join106(projectRoot, ".tmp/context-runtime/extract/candidates");
|
|
110783
110613
|
const visit4 = async (directory) => {
|
|
110784
110614
|
if (!existsSync31(directory))
|
|
110785
110615
|
return;
|
|
110786
|
-
for (const entry of await
|
|
110787
|
-
const path3 =
|
|
110616
|
+
for (const entry of await readdir27(directory, { withFileTypes: true })) {
|
|
110617
|
+
const path3 = join106(directory, entry.name);
|
|
110788
110618
|
if (entry.isDirectory()) {
|
|
110789
110619
|
await visit4(path3);
|
|
110790
110620
|
continue;
|
|
@@ -110792,7 +110622,7 @@ async function pruneExtractRuntime(projectRoot, source2) {
|
|
|
110792
110622
|
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
110793
110623
|
continue;
|
|
110794
110624
|
try {
|
|
110795
|
-
const parsed = JSON.parse(await
|
|
110625
|
+
const parsed = JSON.parse(await readFile85(path3, "utf8"));
|
|
110796
110626
|
const refs = Array.isArray(parsed.source_refs) ? parsed.source_refs : [];
|
|
110797
110627
|
if (parsed.source === source2.name || refs.some((ref2) => typeof ref2 === "string" && stringReferencesSource(ref2, source2))) {
|
|
110798
110628
|
await rm22(path3, { force: true });
|
|
@@ -110834,7 +110664,7 @@ async function removeProjectSource(input) {
|
|
|
110834
110664
|
});
|
|
110835
110665
|
}
|
|
110836
110666
|
await applyAtomicFileBatch({
|
|
110837
|
-
transactionRoot:
|
|
110667
|
+
transactionRoot: join106(input.projectRoot, ".tmp", "context-runtime", "source-remove-transactions"),
|
|
110838
110668
|
writes: [...plan.registryWrite === undefined ? [] : [plan.registryWrite], ...plan.manifestWrite !== undefined ? [plan.manifestWrite] : []],
|
|
110839
110669
|
removals: plan.absoluteRemovals
|
|
110840
110670
|
});
|
|
@@ -110850,7 +110680,7 @@ init_workspace();
|
|
|
110850
110680
|
init_writeLock();
|
|
110851
110681
|
init_durableMultiFileTransaction();
|
|
110852
110682
|
init_durableSingleFileTransaction();
|
|
110853
|
-
import { readFile as
|
|
110683
|
+
import { readFile as readFile86 } from "node:fs/promises";
|
|
110854
110684
|
import ts from "typescript";
|
|
110855
110685
|
function generateSourceConfiguration(text10, selected) {
|
|
110856
110686
|
const file = ts.createSourceFile("index.ts", text10, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
@@ -110990,7 +110820,7 @@ function generateSourceConfiguration(text10, selected) {
|
|
|
110990
110820
|
async function configureRegisteredSources(projectRoot, selected) {
|
|
110991
110821
|
return withProjectWriteLock(projectRoot, "source-project-configuration", async () => {
|
|
110992
110822
|
const path3 = "src/index.ts";
|
|
110993
|
-
const config = JSON.parse(await
|
|
110823
|
+
const config = JSON.parse(await readFile86(await safeProjectTarget(projectRoot, "package.json"), "utf8"));
|
|
110994
110824
|
if (config.context?.entry !== path3)
|
|
110995
110825
|
return {
|
|
110996
110826
|
status: "manual",
|
|
@@ -110999,7 +110829,7 @@ async function configureRegisteredSources(projectRoot, selected) {
|
|
|
110999
110829
|
sources: selected
|
|
111000
110830
|
};
|
|
111001
110831
|
const target = await safeProjectTarget(projectRoot, path3);
|
|
111002
|
-
const text10 = await
|
|
110832
|
+
const text10 = await readFile86(target, "utf8");
|
|
111003
110833
|
const updated = generateSourceConfiguration(text10, selected);
|
|
111004
110834
|
if (updated === undefined)
|
|
111005
110835
|
return {
|
|
@@ -111079,7 +110909,7 @@ async function readIncludeList(projectRoot, path3) {
|
|
|
111079
110909
|
}
|
|
111080
110910
|
let content3;
|
|
111081
110911
|
try {
|
|
111082
|
-
content3 = await
|
|
110912
|
+
content3 = await readFile90(isAbsolute22(trimmed) ? resolve37(trimmed) : resolve37(projectRoot, trimmed), "utf8");
|
|
111083
110913
|
} catch (error) {
|
|
111084
110914
|
throw new ContextError(ExitCode.UserError, `cannot read include list: ${trimmed}`, {
|
|
111085
110915
|
category: ErrorCategory.UserInputInvalid,
|
|
@@ -111147,7 +110977,7 @@ function writeFormatted(value, format2) {
|
|
|
111147
110977
|
return;
|
|
111148
110978
|
}
|
|
111149
110979
|
if (format2 === "yaml") {
|
|
111150
|
-
process.stdout.write(
|
|
110980
|
+
process.stdout.write(import_yaml50.default.stringify(value));
|
|
111151
110981
|
return;
|
|
111152
110982
|
}
|
|
111153
110983
|
process.stdout.write(renderTable2(value));
|
|
@@ -111337,14 +111167,14 @@ report shown above. A direct maintenance call outside that Route may omit it.
|
|
|
111337
111167
|
const url = optionalString2(options.url);
|
|
111338
111168
|
const docToken = optionalString2(options.docToken);
|
|
111339
111169
|
const wikiToken = optionalString2(options.wikiToken);
|
|
111340
|
-
const
|
|
111170
|
+
const title2 = optionalString2(options.title);
|
|
111341
111171
|
const requestedModule = optionalString2(options.module);
|
|
111342
111172
|
const batchMode = sourceNamespace.generated || isDateSourceNamespace(sourceNamespace.name) || requestedModule !== undefined;
|
|
111343
111173
|
const module = batchMode ? requestedModule ?? defaultLarkModule({
|
|
111344
111174
|
...url !== undefined ? { url } : {},
|
|
111345
111175
|
...docToken !== undefined ? { docToken } : {},
|
|
111346
111176
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
111347
|
-
...
|
|
111177
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
111348
111178
|
}) : undefined;
|
|
111349
111179
|
const sourceName = module === undefined ? sourceNamespace.name : `${sourceNamespace.name}/${module}`;
|
|
111350
111180
|
const result = await addLarkSource({
|
|
@@ -111354,7 +111184,7 @@ report shown above. A direct maintenance call outside that Route may omit it.
|
|
|
111354
111184
|
...url !== undefined ? { url } : {},
|
|
111355
111185
|
...docToken !== undefined ? { docToken } : {},
|
|
111356
111186
|
...wikiToken !== undefined ? { wikiToken } : {},
|
|
111357
|
-
...
|
|
111187
|
+
...title2 !== undefined ? { title: title2 } : {}
|
|
111358
111188
|
});
|
|
111359
111189
|
writeFormatted(options.configure ? { ...result, configuration: await configureRegisteredSources(projectRoot, [{ type: "lark", name: sourceName }]) } : result, format2);
|
|
111360
111190
|
});
|
|
@@ -111468,19 +111298,19 @@ report shown above. A direct maintenance call outside that Route may omit it.
|
|
|
111468
111298
|
init_cliFeedback();
|
|
111469
111299
|
init_errors3();
|
|
111470
111300
|
init_exitCode();
|
|
111471
|
-
var
|
|
111301
|
+
var import_yaml52 = __toESM(require_dist(), 1);
|
|
111472
111302
|
|
|
111473
111303
|
// src/project/productionSkillCatalog.ts
|
|
111474
111304
|
init_errors3();
|
|
111475
111305
|
init_cliFeedback();
|
|
111476
111306
|
init_exitCode();
|
|
111477
|
-
var
|
|
111307
|
+
var import_yaml51 = __toESM(require_dist(), 1);
|
|
111478
111308
|
import { constants as constants6, existsSync as existsSync32 } from "node:fs";
|
|
111479
|
-
import { lstat as lstat13, open as open5, readdir as
|
|
111480
|
-
import { dirname as
|
|
111309
|
+
import { lstat as lstat13, open as open5, readdir as readdir28 } from "node:fs/promises";
|
|
111310
|
+
import { dirname as dirname43, join as join108, resolve as resolve38 } from "node:path";
|
|
111481
111311
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
111482
111312
|
function bundledSkillRoot() {
|
|
111483
|
-
const directory =
|
|
111313
|
+
const directory = dirname43(fileURLToPath8(import.meta.url));
|
|
111484
111314
|
const root2 = [resolve38(directory, "indexers/bundles"), resolve38(directory, "../../dist/indexers/bundles")].find((path3) => existsSync32(path3));
|
|
111485
111315
|
if (!root2)
|
|
111486
111316
|
throw new TypeError("Bundled skill files are unavailable; build or reinstall the Context CLI.");
|
|
@@ -111504,10 +111334,10 @@ async function listProductionSkills(root2) {
|
|
|
111504
111334
|
}
|
|
111505
111335
|
}
|
|
111506
111336
|
async function readProductionSkills(root2) {
|
|
111507
|
-
const entries2 = (await
|
|
111337
|
+
const entries2 = (await readdir28(root2, { withFileTypes: true })).filter((entry) => entry.isDirectory()).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
111508
111338
|
const skills = [];
|
|
111509
111339
|
for (const directory of entries2) {
|
|
111510
|
-
const entry =
|
|
111340
|
+
const entry = join108(root2, directory.name, "SKILL.md");
|
|
111511
111341
|
if (!(await lstat13(entry)).isFile())
|
|
111512
111342
|
throw new TypeError(`Skill entry must be a regular file: ${entry}`);
|
|
111513
111343
|
const handle2 = await open5(entry, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
|
|
@@ -111524,7 +111354,7 @@ async function readProductionSkills(root2) {
|
|
|
111524
111354
|
const frontmatter2 = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(header);
|
|
111525
111355
|
if (!frontmatter2)
|
|
111526
111356
|
throw new TypeError(`Skill entry needs complete frontmatter within 64 KiB: ${entry}`);
|
|
111527
|
-
const value =
|
|
111357
|
+
const value = import_yaml51.default.parse(frontmatter2[1], { uniqueKeys: true });
|
|
111528
111358
|
if (!value || typeof value !== "object" || !("name" in value) || typeof value.name !== "string" || !value.name.trim() || !("description" in value) || typeof value.description !== "string" || !value.description.trim()) {
|
|
111529
111359
|
throw new TypeError(`Skill entry needs a name and description: ${entry}`);
|
|
111530
111360
|
}
|
|
@@ -111587,7 +111417,7 @@ function outputFormat2(options) {
|
|
|
111587
111417
|
}
|
|
111588
111418
|
function writeOutput(value, format2) {
|
|
111589
111419
|
process.stdout.write(format2 === "json" ? `${JSON.stringify(value, null, 2)}
|
|
111590
|
-
` :
|
|
111420
|
+
` : import_yaml52.default.stringify(value));
|
|
111591
111421
|
}
|
|
111592
111422
|
function registerProjectIndexerCommands(program2) {
|
|
111593
111423
|
const indexer = program2.command("indexer").description("Discover bundled skills or inspect a benchmark result");
|
|
@@ -111642,7 +111472,7 @@ init_exitCode();
|
|
|
111642
111472
|
init_workflowProvider();
|
|
111643
111473
|
init_workspace();
|
|
111644
111474
|
import { existsSync as existsSync33 } from "node:fs";
|
|
111645
|
-
import { dirname as
|
|
111475
|
+
import { dirname as dirname44, resolve as resolve39 } from "node:path";
|
|
111646
111476
|
function shellQuote7(value) {
|
|
111647
111477
|
return /^[A-Za-z0-9._/=-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
111648
111478
|
}
|
|
@@ -111675,10 +111505,10 @@ function readyResult(input, projectRoot, relocation) {
|
|
|
111675
111505
|
return {
|
|
111676
111506
|
schema: "context.entry.v1",
|
|
111677
111507
|
guidance: {
|
|
111678
|
-
knowledge_updates: { path: resolve39(
|
|
111679
|
-
workspace_prepare: { path: resolve39(
|
|
111680
|
-
workspace_commit: { path: resolve39(
|
|
111681
|
-
workspace_restore: { path: resolve39(
|
|
111508
|
+
knowledge_updates: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/knowledge-updates.md") },
|
|
111509
|
+
workspace_prepare: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/workspace-prepare.md") },
|
|
111510
|
+
workspace_commit: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/workspace-commit.md") },
|
|
111511
|
+
workspace_restore: { path: resolve39(dirname44(contextWorkflowProviderPath()), "resources/procedures/workspace-restore.md") }
|
|
111682
111512
|
},
|
|
111683
111513
|
state: relocation ? "workspace-relocation-required" : "workspace-ready",
|
|
111684
111514
|
cwd: resolve39(input.cwd),
|
|
@@ -111915,20 +111745,20 @@ init_cliFeedback();
|
|
|
111915
111745
|
init_errors3();
|
|
111916
111746
|
init_exitCode();
|
|
111917
111747
|
import { existsSync as existsSync35 } from "node:fs";
|
|
111918
|
-
import { dirname as
|
|
111748
|
+
import { dirname as dirname46, join as join110, resolve as resolve40 } from "node:path";
|
|
111919
111749
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
111920
111750
|
|
|
111921
111751
|
// src/project/pluginInstallTargets.ts
|
|
111922
111752
|
init_cliFeedback();
|
|
111923
111753
|
init_errors3();
|
|
111924
111754
|
init_exitCode();
|
|
111925
|
-
import { execFile as
|
|
111755
|
+
import { execFile as execFile13 } from "node:child_process";
|
|
111926
111756
|
import { existsSync as existsSync34 } from "node:fs";
|
|
111927
|
-
import { cp as cp2, mkdir as mkdir35, readdir as
|
|
111757
|
+
import { cp as cp2, mkdir as mkdir35, readdir as readdir29, readFile as readFile91, rename as rename9, rm as rm23, writeFile as writeFile27 } from "node:fs/promises";
|
|
111928
111758
|
import { homedir as homedir2 } from "node:os";
|
|
111929
|
-
import { dirname as
|
|
111930
|
-
import { promisify as
|
|
111931
|
-
var execFileAsync7 =
|
|
111759
|
+
import { dirname as dirname45, join as join109 } from "node:path";
|
|
111760
|
+
import { promisify as promisify13 } from "node:util";
|
|
111761
|
+
var execFileAsync7 = promisify13(execFile13);
|
|
111932
111762
|
var MARKETPLACE_NAME = "c4a";
|
|
111933
111763
|
var PLUGIN_ID = "c4a@c4a";
|
|
111934
111764
|
var PLUGIN_NAME = "c4a";
|
|
@@ -111980,23 +111810,23 @@ async function claudePluginInstalled(pluginId) {
|
|
|
111980
111810
|
}
|
|
111981
111811
|
}
|
|
111982
111812
|
function codexHome() {
|
|
111983
|
-
return process.env.CODEX_HOME?.trim() ||
|
|
111813
|
+
return process.env.CODEX_HOME?.trim() || join109(homedir2(), ".codex");
|
|
111984
111814
|
}
|
|
111985
111815
|
function claudePluginCacheRoot() {
|
|
111986
111816
|
const explicitRoot = process.env[CLAUDE_PLUGIN_CACHE_ROOT_ENV2]?.trim();
|
|
111987
111817
|
if (explicitRoot)
|
|
111988
111818
|
return explicitRoot;
|
|
111989
111819
|
const home = process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV2]?.trim() || homedir2();
|
|
111990
|
-
return
|
|
111820
|
+
return join109(home, ".claude", "plugins", "cache");
|
|
111991
111821
|
}
|
|
111992
111822
|
function sharedSkillsRoot() {
|
|
111993
|
-
return process.env[SHARED_SKILLS_ROOT_ENV]?.trim() ||
|
|
111823
|
+
return process.env[SHARED_SKILLS_ROOT_ENV]?.trim() || join109(homedir2(), ".agents", "skills");
|
|
111994
111824
|
}
|
|
111995
111825
|
function claudeSkillsRoot() {
|
|
111996
|
-
return process.env[CLAUDE_SKILLS_ROOT_ENV]?.trim() ||
|
|
111826
|
+
return process.env[CLAUDE_SKILLS_ROOT_ENV]?.trim() || join109(homedir2(), ".claude", "skills");
|
|
111997
111827
|
}
|
|
111998
111828
|
function cursorPluginRoot() {
|
|
111999
|
-
return process.env[CURSOR_PLUGIN_ROOT_ENV]?.trim() ||
|
|
111829
|
+
return process.env[CURSOR_PLUGIN_ROOT_ENV]?.trim() || join109(homedir2(), ".cursor", "plugins", "local", PLUGIN_NAME);
|
|
112000
111830
|
}
|
|
112001
111831
|
function blockHeader(line) {
|
|
112002
111832
|
const match = line.match(/^\s*\[([^\]]+)\]\s*$/u);
|
|
@@ -112047,8 +111877,8 @@ function pruneLegacyCodexConfigContent(content3) {
|
|
|
112047
111877
|
`), removed };
|
|
112048
111878
|
}
|
|
112049
111879
|
async function pruneLegacyCodexConfig(dryRun, steps) {
|
|
112050
|
-
const configPath =
|
|
112051
|
-
const current2 = await
|
|
111880
|
+
const configPath = join109(codexHome(), "config.toml");
|
|
111881
|
+
const current2 = await readFile91(configPath, "utf8").catch(() => "");
|
|
112052
111882
|
if (!current2)
|
|
112053
111883
|
return;
|
|
112054
111884
|
const next2 = pruneLegacyCodexConfigContent(current2);
|
|
@@ -112064,10 +111894,10 @@ async function pruneLegacyCodexConfig(dryRun, steps) {
|
|
|
112064
111894
|
}
|
|
112065
111895
|
}
|
|
112066
111896
|
async function pruneCodexPluginCacheForName(pluginName, keepVersion, dryRun, steps) {
|
|
112067
|
-
const cacheRoot =
|
|
111897
|
+
const cacheRoot = join109(codexHome(), "plugins", "cache", MARKETPLACE_NAME, pluginName);
|
|
112068
111898
|
if (!existsSync34(cacheRoot))
|
|
112069
111899
|
return;
|
|
112070
|
-
const versions = (await
|
|
111900
|
+
const versions = (await readdir29(cacheRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== keepVersion).map((entry) => entry.name).sort();
|
|
112071
111901
|
if (versions.length === 0)
|
|
112072
111902
|
return;
|
|
112073
111903
|
steps.push({
|
|
@@ -112076,7 +111906,7 @@ async function pruneCodexPluginCacheForName(pluginName, keepVersion, dryRun, ste
|
|
|
112076
111906
|
status: dryRun ? "planned" : "ran"
|
|
112077
111907
|
});
|
|
112078
111908
|
if (!dryRun)
|
|
112079
|
-
await Promise.all(versions.map((version3) => rm23(
|
|
111909
|
+
await Promise.all(versions.map((version3) => rm23(join109(cacheRoot, version3), { recursive: true, force: true })));
|
|
112080
111910
|
}
|
|
112081
111911
|
async function pruneCodexPluginCache(currentVersion, dryRun, steps) {
|
|
112082
111912
|
await pruneCodexPluginCacheForName(PLUGIN_NAME, currentVersion, dryRun, steps);
|
|
@@ -112086,7 +111916,7 @@ async function pruneCodexPluginCache(currentVersion, dryRun, steps) {
|
|
|
112086
111916
|
}
|
|
112087
111917
|
async function isEmptyDir2(dir) {
|
|
112088
111918
|
try {
|
|
112089
|
-
return (await
|
|
111919
|
+
return (await readdir29(dir)).length === 0;
|
|
112090
111920
|
} catch {
|
|
112091
111921
|
return false;
|
|
112092
111922
|
}
|
|
@@ -112096,19 +111926,19 @@ async function pruneClaudeOrphanContextCache(dryRun, steps) {
|
|
|
112096
111926
|
if (!existsSync34(cacheRoot))
|
|
112097
111927
|
return;
|
|
112098
111928
|
const removed = [];
|
|
112099
|
-
const marketplaces = await
|
|
111929
|
+
const marketplaces = await readdir29(cacheRoot, { withFileTypes: true }).catch(() => []);
|
|
112100
111930
|
for (const marketplace of marketplaces) {
|
|
112101
111931
|
if (!marketplace.isDirectory())
|
|
112102
111932
|
continue;
|
|
112103
|
-
const pluginDir =
|
|
111933
|
+
const pluginDir = join109(cacheRoot, marketplace.name, PLUGIN_NAME);
|
|
112104
111934
|
if (!existsSync34(pluginDir))
|
|
112105
111935
|
continue;
|
|
112106
|
-
const versions = await
|
|
111936
|
+
const versions = await readdir29(pluginDir, { withFileTypes: true }).catch(() => []);
|
|
112107
111937
|
for (const version3 of versions) {
|
|
112108
111938
|
if (!version3.isDirectory())
|
|
112109
111939
|
continue;
|
|
112110
|
-
const versionDir =
|
|
112111
|
-
if (!existsSync34(
|
|
111940
|
+
const versionDir = join109(pluginDir, version3.name);
|
|
111941
|
+
if (!existsSync34(join109(versionDir, ORPHAN_MARKER2)))
|
|
112112
111942
|
continue;
|
|
112113
111943
|
removed.push(`${marketplace.name}/${PLUGIN_NAME}/${version3.name}`);
|
|
112114
111944
|
if (!dryRun) {
|
|
@@ -112118,7 +111948,7 @@ async function pruneClaudeOrphanContextCache(dryRun, steps) {
|
|
|
112118
111948
|
if (!dryRun && await isEmptyDir2(pluginDir)) {
|
|
112119
111949
|
await rm23(pluginDir, { recursive: true, force: true });
|
|
112120
111950
|
}
|
|
112121
|
-
const marketplaceDir =
|
|
111951
|
+
const marketplaceDir = join109(cacheRoot, marketplace.name);
|
|
112122
111952
|
if (!dryRun && await isEmptyDir2(marketplaceDir)) {
|
|
112123
111953
|
await rm23(marketplaceDir, { recursive: true, force: true });
|
|
112124
111954
|
}
|
|
@@ -112139,7 +111969,7 @@ async function pruneClaudeLegacyPluginCache(dryRun, steps) {
|
|
|
112139
111969
|
return;
|
|
112140
111970
|
const removed = [];
|
|
112141
111971
|
for (const pluginName of LEGACY_PLUGIN_NAMES) {
|
|
112142
|
-
const pluginDir =
|
|
111972
|
+
const pluginDir = join109(cacheRoot, MARKETPLACE_NAME, pluginName);
|
|
112143
111973
|
if (!existsSync34(pluginDir))
|
|
112144
111974
|
continue;
|
|
112145
111975
|
removed.push(`${MARKETPLACE_NAME}/${pluginName}`);
|
|
@@ -112156,12 +111986,12 @@ async function pruneClaudeLegacyPluginCache(dryRun, steps) {
|
|
|
112156
111986
|
}
|
|
112157
111987
|
}
|
|
112158
111988
|
async function pruneClaudeSupersededContextCache(root2, dryRun, steps) {
|
|
112159
|
-
const manifest = await
|
|
111989
|
+
const manifest = await readFile91(join109(root2, "claude", ".claude-plugin", "plugin.json"), "utf8").then((content3) => JSON.parse(content3)).catch(() => null);
|
|
112160
111990
|
const currentVersion = typeof manifest?.version === "string" ? manifest.version : null;
|
|
112161
111991
|
if (!currentVersion)
|
|
112162
111992
|
return;
|
|
112163
|
-
const pluginDir =
|
|
112164
|
-
const staleVersions = (await
|
|
111993
|
+
const pluginDir = join109(claudePluginCacheRoot(), MARKETPLACE_NAME, PLUGIN_NAME);
|
|
111994
|
+
const staleVersions = (await readdir29(pluginDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== currentVersion).map((entry) => entry.name);
|
|
112165
111995
|
if (staleVersions.length === 0)
|
|
112166
111996
|
return;
|
|
112167
111997
|
steps.push({
|
|
@@ -112170,7 +112000,7 @@ async function pruneClaudeSupersededContextCache(root2, dryRun, steps) {
|
|
|
112170
112000
|
status: dryRun ? "planned" : "ran"
|
|
112171
112001
|
});
|
|
112172
112002
|
if (!dryRun) {
|
|
112173
|
-
await Promise.all(staleVersions.map((version3) => rm23(
|
|
112003
|
+
await Promise.all(staleVersions.map((version3) => rm23(join109(pluginDir, version3), { recursive: true, force: true })));
|
|
112174
112004
|
}
|
|
112175
112005
|
}
|
|
112176
112006
|
function enableCodexPluginConfig(content3) {
|
|
@@ -112234,36 +112064,36 @@ source = ${JSON.stringify(root2)}
|
|
|
112234
112064
|
`;
|
|
112235
112065
|
}
|
|
112236
112066
|
async function ensureCodexPluginEnabled() {
|
|
112237
|
-
const configPath =
|
|
112238
|
-
await mkdir35(
|
|
112239
|
-
const current2 = await
|
|
112067
|
+
const configPath = join109(codexHome(), "config.toml");
|
|
112068
|
+
await mkdir35(dirname45(configPath), { recursive: true });
|
|
112069
|
+
const current2 = await readFile91(configPath, "utf8").catch(() => "");
|
|
112240
112070
|
const next2 = enableCodexPluginConfig(current2);
|
|
112241
112071
|
if (next2 !== current2) {
|
|
112242
112072
|
await writeFile27(configPath, next2, "utf8");
|
|
112243
112073
|
}
|
|
112244
112074
|
}
|
|
112245
112075
|
async function ensureCodexLocalMarketplace(root2) {
|
|
112246
|
-
const configPath =
|
|
112247
|
-
await mkdir35(
|
|
112248
|
-
const current2 = await
|
|
112076
|
+
const configPath = join109(codexHome(), "config.toml");
|
|
112077
|
+
await mkdir35(dirname45(configPath), { recursive: true });
|
|
112078
|
+
const current2 = await readFile91(configPath, "utf8").catch(() => "");
|
|
112249
112079
|
const next2 = upsertCodexLocalMarketplaceConfig(current2, root2);
|
|
112250
112080
|
if (next2 !== current2) {
|
|
112251
112081
|
await writeFile27(configPath, next2, "utf8");
|
|
112252
112082
|
}
|
|
112253
112083
|
}
|
|
112254
112084
|
async function codexPluginVersion(root2) {
|
|
112255
|
-
const manifestPath =
|
|
112256
|
-
const manifest = JSON.parse(await
|
|
112085
|
+
const manifestPath = join109(root2, "codex", ".codex-plugin", "plugin.json");
|
|
112086
|
+
const manifest = JSON.parse(await readFile91(manifestPath, "utf8"));
|
|
112257
112087
|
if (typeof manifest.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(manifest.version)) {
|
|
112258
112088
|
throw new Error(`Codex plugin manifest has an invalid version: ${manifestPath}`);
|
|
112259
112089
|
}
|
|
112260
112090
|
return manifest.version;
|
|
112261
112091
|
}
|
|
112262
112092
|
function codexPluginCacheDir(version3) {
|
|
112263
|
-
return
|
|
112093
|
+
return join109(codexHome(), "plugins", "cache", MARKETPLACE_NAME, PLUGIN_NAME, version3);
|
|
112264
112094
|
}
|
|
112265
112095
|
async function replaceDirectoryFromSource(source2, target) {
|
|
112266
|
-
await mkdir35(
|
|
112096
|
+
await mkdir35(dirname45(target), { recursive: true });
|
|
112267
112097
|
const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
112268
112098
|
const previous3 = `${target}.previous-${process.pid}-${Date.now()}`;
|
|
112269
112099
|
await rm23(temporary, { recursive: true, force: true });
|
|
@@ -112283,7 +112113,7 @@ async function replaceDirectoryFromSource(source2, target) {
|
|
|
112283
112113
|
}
|
|
112284
112114
|
}
|
|
112285
112115
|
async function materializeCodexPluginCache(root2, version3, dryRun, steps) {
|
|
112286
|
-
const source2 =
|
|
112116
|
+
const source2 = join109(root2, "codex");
|
|
112287
112117
|
const target = codexPluginCacheDir(version3);
|
|
112288
112118
|
steps.push({
|
|
112289
112119
|
agent: "codex",
|
|
@@ -112295,16 +112125,16 @@ async function materializeCodexPluginCache(root2, version3, dryRun, steps) {
|
|
|
112295
112125
|
await replaceDirectoryFromSource(source2, target);
|
|
112296
112126
|
}
|
|
112297
112127
|
async function bundledProviderSkillNames(root2) {
|
|
112298
|
-
const skillsRoot =
|
|
112299
|
-
const entries2 = await
|
|
112128
|
+
const skillsRoot = join109(root2, "skills");
|
|
112129
|
+
const entries2 = await readdir29(skillsRoot, { withFileTypes: true });
|
|
112300
112130
|
const names = [];
|
|
112301
112131
|
for (const entry of entries2) {
|
|
112302
112132
|
if (!entry.isDirectory() || entry.name === "context")
|
|
112303
112133
|
continue;
|
|
112304
|
-
const skillPath =
|
|
112134
|
+
const skillPath = join109(skillsRoot, entry.name, "SKILL.md");
|
|
112305
112135
|
if (!existsSync34(skillPath))
|
|
112306
112136
|
continue;
|
|
112307
|
-
const skill = await
|
|
112137
|
+
const skill = await readFile91(skillPath, "utf8");
|
|
112308
112138
|
if (!/^\s*context-role:\s*["']?indexer-provider["']?\s*$/mu.test(skill))
|
|
112309
112139
|
continue;
|
|
112310
112140
|
names.push(entry.name);
|
|
@@ -112316,10 +112146,10 @@ async function bundledProviderSkillNames(root2) {
|
|
|
112316
112146
|
return names;
|
|
112317
112147
|
}
|
|
112318
112148
|
async function materializeProviderSkills(root2, targetRoot, agent, dryRun, steps) {
|
|
112319
|
-
const sourceRoot2 =
|
|
112149
|
+
const sourceRoot2 = join109(root2, "skills");
|
|
112320
112150
|
for (const name3 of await bundledProviderSkillNames(root2)) {
|
|
112321
|
-
const source2 =
|
|
112322
|
-
const target =
|
|
112151
|
+
const source2 = join109(sourceRoot2, name3);
|
|
112152
|
+
const target = join109(targetRoot, name3);
|
|
112323
112153
|
steps.push({
|
|
112324
112154
|
agent,
|
|
112325
112155
|
command: `materialize lifecycle Provider skill: ${shellQuote8(source2)} -> ${shellQuote8(target)}`,
|
|
@@ -112330,7 +112160,7 @@ async function materializeProviderSkills(root2, targetRoot, agent, dryRun, steps
|
|
|
112330
112160
|
}
|
|
112331
112161
|
}
|
|
112332
112162
|
async function installCursor(root2, dryRun, steps) {
|
|
112333
|
-
const source2 =
|
|
112163
|
+
const source2 = join109(root2, "cursor");
|
|
112334
112164
|
const target = cursorPluginRoot();
|
|
112335
112165
|
steps.push({
|
|
112336
112166
|
agent: "cursor",
|
|
@@ -112385,12 +112215,12 @@ async function installCodex(root2, dryRun, steps) {
|
|
|
112385
112215
|
steps.push({ agent: "codex", command: commandLine("codex", addArgs), status: dryRun ? "planned" : "ran" });
|
|
112386
112216
|
steps.push({
|
|
112387
112217
|
agent: "codex",
|
|
112388
|
-
command: `ensure ${shellQuote8(
|
|
112218
|
+
command: `ensure ${shellQuote8(join109(codexHome(), "config.toml"))} registers local marketplace ${shellQuote8(MARKETPLACE_NAME)}`,
|
|
112389
112219
|
status: dryRun ? "planned" : "ran"
|
|
112390
112220
|
});
|
|
112391
112221
|
steps.push({
|
|
112392
112222
|
agent: "codex",
|
|
112393
|
-
command: `ensure ${shellQuote8(
|
|
112223
|
+
command: `ensure ${shellQuote8(join109(codexHome(), "config.toml"))} enables ${shellQuote8(PLUGIN_ID)}`,
|
|
112394
112224
|
status: dryRun ? "planned" : "ran"
|
|
112395
112225
|
});
|
|
112396
112226
|
if (dryRun) {
|
|
@@ -112429,10 +112259,10 @@ function pluginAgentOption(value) {
|
|
|
112429
112259
|
}
|
|
112430
112260
|
function packageCandidateDirs() {
|
|
112431
112261
|
const dirs = [];
|
|
112432
|
-
let dir =
|
|
112262
|
+
let dir = dirname46(fileURLToPath9(import.meta.url));
|
|
112433
112263
|
for (let index2 = 0;index2 < 8; index2++) {
|
|
112434
112264
|
dirs.push(dir);
|
|
112435
|
-
const parent =
|
|
112265
|
+
const parent = dirname46(dir);
|
|
112436
112266
|
if (parent === dir)
|
|
112437
112267
|
break;
|
|
112438
112268
|
dir = parent;
|
|
@@ -112445,13 +112275,13 @@ function pluginRootCandidates() {
|
|
|
112445
112275
|
return [resolve40(envRoot)];
|
|
112446
112276
|
const candidates = [];
|
|
112447
112277
|
for (const dir of packageCandidateDirs()) {
|
|
112448
|
-
candidates.push(
|
|
112449
|
-
candidates.push(
|
|
112278
|
+
candidates.push(join110(dir, "plugins"));
|
|
112279
|
+
candidates.push(join110(dir, "dist", "plugins"));
|
|
112450
112280
|
}
|
|
112451
112281
|
return [...new Set(candidates)];
|
|
112452
112282
|
}
|
|
112453
112283
|
function isInstallablePluginRoot(root2) {
|
|
112454
|
-
return existsSync35(
|
|
112284
|
+
return existsSync35(join110(root2, ".claude-plugin", "marketplace.json")) && existsSync35(join110(root2, ".agents", "plugins", "marketplace.json")) && existsSync35(join110(root2, "claude", ".claude-plugin", "plugin.json")) && existsSync35(join110(root2, "codex", ".codex-plugin", "plugin.json")) && existsSync35(join110(root2, "cursor", ".cursor-plugin", "plugin.json")) && existsSync35(join110(root2, "skills"));
|
|
112455
112285
|
}
|
|
112456
112286
|
function resolveBundledPluginsRoot() {
|
|
112457
112287
|
const candidates = pluginRootCandidates();
|
|
@@ -112562,32 +112392,32 @@ function formatPluginStatusResult(result) {
|
|
|
112562
112392
|
function formatPluginInstallResult(result) {
|
|
112563
112393
|
const degraded = result.results.some((item) => item.status === "skipped" || item.status === "failed");
|
|
112564
112394
|
const ready = result.results.filter((item) => item.status === "installed" || item.status === "planned").map((item) => item.agent);
|
|
112565
|
-
const
|
|
112395
|
+
const body2 = [
|
|
112566
112396
|
`marketplace: ${dim(result.pluginsRoot)}`,
|
|
112567
112397
|
"manual install: use the marketplace path above as the plugin marketplace root."
|
|
112568
112398
|
];
|
|
112569
112399
|
for (const item of result.results) {
|
|
112570
112400
|
if (item.status === "installed" || item.status === "planned") {
|
|
112571
|
-
|
|
112401
|
+
body2.push(`✅ ${item.agent}: ${item.status}`);
|
|
112572
112402
|
continue;
|
|
112573
112403
|
}
|
|
112574
112404
|
const icon = item.status === "failed" ? "✗" : "⚠";
|
|
112575
112405
|
const detail = item.message ? ` — ${item.message}` : "";
|
|
112576
|
-
|
|
112406
|
+
body2.push(yellow(`${icon} ${item.agent}: ${item.status}${detail}`));
|
|
112577
112407
|
if (item.next)
|
|
112578
|
-
|
|
112408
|
+
body2.push(yellow(` next: ${item.next}`));
|
|
112579
112409
|
}
|
|
112580
112410
|
const detailSteps = result.steps.map((step) => ` ${step.agent}: ${step.status} ${step.command}`);
|
|
112581
112411
|
if (detailSteps.length > 0) {
|
|
112582
|
-
|
|
112583
|
-
|
|
112412
|
+
body2.push("details:");
|
|
112413
|
+
body2.push(...detailSteps.map(dim));
|
|
112584
112414
|
}
|
|
112585
112415
|
return formatFeedback({
|
|
112586
112416
|
symbol: result.dryRun ? "·" : degraded ? "⚠" : "✓",
|
|
112587
112417
|
action: result.dryRun ? "planned" : "installed",
|
|
112588
112418
|
subject: "context plugin",
|
|
112589
112419
|
headline: `${ready.length}/${result.agents.length} target(s) ready`,
|
|
112590
|
-
body
|
|
112420
|
+
body: body2
|
|
112591
112421
|
});
|
|
112592
112422
|
}
|
|
112593
112423
|
|
|
@@ -112687,29 +112517,29 @@ function inferErrorCategory(message) {
|
|
|
112687
112517
|
}
|
|
112688
112518
|
function readQuickstartPath() {
|
|
112689
112519
|
try {
|
|
112690
|
-
let dir =
|
|
112520
|
+
let dir = dirname47(fileURLToPath10(import.meta.url));
|
|
112691
112521
|
for (let i2 = 0;i2 < 8; i2++) {
|
|
112692
|
-
const candidate =
|
|
112522
|
+
const candidate = join111(dir, "docs", "quickstart.md");
|
|
112693
112523
|
if (existsSync36(candidate))
|
|
112694
112524
|
return candidate;
|
|
112695
|
-
const pkg =
|
|
112525
|
+
const pkg = join111(dir, "package.json");
|
|
112696
112526
|
if (existsSync36(pkg))
|
|
112697
112527
|
return candidate;
|
|
112698
|
-
const parent =
|
|
112528
|
+
const parent = dirname47(dir);
|
|
112699
112529
|
if (parent === dir)
|
|
112700
112530
|
break;
|
|
112701
112531
|
dir = parent;
|
|
112702
112532
|
}
|
|
112703
112533
|
} catch {}
|
|
112704
|
-
return
|
|
112534
|
+
return join111(dirname47(fileURLToPath10(import.meta.url)), "docs", "quickstart.md");
|
|
112705
112535
|
}
|
|
112706
112536
|
var GREEN = "\x1B[32m";
|
|
112707
112537
|
var RESET = "\x1B[0m";
|
|
112708
112538
|
function greenBox(lines) {
|
|
112709
112539
|
const width = Math.max(...lines.map((line) => line.length));
|
|
112710
112540
|
const border = `+${"-".repeat(width + 2)}+`;
|
|
112711
|
-
const
|
|
112712
|
-
return `${GREEN}${[border, ...
|
|
112541
|
+
const body2 = lines.map((line) => `| ${line.padEnd(width)} |`);
|
|
112542
|
+
return `${GREEN}${[border, ...body2, border].join(`
|
|
112713
112543
|
`)}${RESET}`;
|
|
112714
112544
|
}
|
|
112715
112545
|
function headerHelpText() {
|